Compare commits

..
2048 Commits
Author SHA1 Message Date
Evgeny Poberezkin ac4bf690b6 7.1.0.4 2026-08-20 09:33:10 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 0d3cf39c27 agent: fast queue rotation not requiring the current server to be online (#1847)
* agent: fast queue rotation does not requiring the current server to be online

* update plan

* update plan and rfc

* corrections

* update plan

* implementation

* test

* refactor

* test switching notificaitons

* rename

* fix, refactor

* fix possible crash

* simplify

* refactor

* simplify

* refactor

* simplify

* order, avoid re-reading connection

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-08-20 09:02:41 +01:00
Evgeny Poberezkin c5ff829cfb 7.1.0.3 2026-08-18 13:57:10 +01:00
Evgenyandsh d95eb47a4d agent: drop versions before 07/2024 (support of PQ encryption and securing reply queue) (#1841)
* agent: drop versions before 07/2024 (support of PQ encryption and securing reply queue)

* fix encoding, add error context

* fix agent tests

* fix test

---------

Co-authored-by: sh <github.shum@liber.li>
2026-08-17 12:51:45 +01:00
spaced4ndy c377f2c1b1 xftp: prevent sender from writing files outside the destination folder (#1850) 2026-08-12 18:39:03 +01:00
Evgeny Poberezkin 413f30bee5 7.1.0.2 2026-08-12 10:58:20 +01:00
sh 3996472494 build: expose Server.Information for client_library (#1849) 2026-08-12 10:57:48 +01:00
Evgeny Poberezkin 21568ab277 7.1.0.1 2026-08-10 09:12:44 +01:00
Ed Asriyan e3d53428a0 smp server: fix handshake compatibility by moving server info to SMP v21 (#1845) 2026-08-08 21:27:02 +01:00
spaced4ndy a908de6416 server: prevent concurrent SKEY commands from overwriting queue sender key (#1844)
* server: prevent concurrent SKEY commands from overwriting queue sender key

* wip
2026-08-07 22:27:24 +01:00
Narasimha-scandEvgeny 33c458ffd4 agent: report PRXY broker errors as proxy-to-relay errors (#1842)
* agent: report PRXY broker errors as proxy-to-relay errors

The proxy returns PROXY BROKER errors only when it fails to connect to the
destination relay, but for PRXY they were mapped to SMP <proxy> (PROXY ...),
losing the relay address, so the clients reported them as errors of the
connection to the forwarding server. Map them to PROXY {proxyServer,
relayServer, ...}, the same shape PFWD errors already use.

* Apply suggestion from @epoberezkin

* agent: keep ProxyProtocolError for PRXY broker errors

Reverts 826a2377.

ProxyResponseError wraps PCEResponseError, a response that failed to
parse, and both clients match protocolError when rendering this error,
so the connect alert falls through to the raw error dump - and released
clients cannot render it at all. temporaryAgentError and serverHostError
also match ProxyProtocolError only, so the failure stops being retried
and proxy fallback no longer engages.

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2026-08-06 12:23:44 +01:00
EvgenyandEd Asriyan 0e13d46a2a smp-server: add serverInfoBytes to THandleParams and related functions (#1836)
* smp-server: add serverInfoBytes to THandleParams and related functions

* server information

* fix test

---------

Co-authored-by: Ed Asriyan <service.github@asriyan.me>
2026-08-02 11:45:04 +01:00
Evgeny Poberezkin ee4dd0d8de 7.1.0.0 2026-08-01 12:15:58 +01:00
EvgenyEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>
e4e5ce75fa agent: use double ratchet from the first message in contact addresses, request rejection, RPC (#1831)
* agent: initialize double ratchet from the invitation via contact address (#1829)

* rfc: agent support for PRC pattern without duplex connection

* update rfc, plan

* corrections

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

* split rfcs, add plan

* update rfc

* update rfc and plan

* update plan

* add double ratchet keys to address

* agent schema

* fix version in test

* change ContactRequest encoding

* fix tests

* update plan

* tests, refactor

* fix compilation

* refactor

* refactor more

* fix

* add export

* fix

* refactor

* refactor

* update schema

* rename

* refactor

* refactor 2

* comment, type

* type

* rename

* refactor more

* move type

* refactor

* remove comment

* diff

* refactor

* comment

* puns

* get correct key

* rename

* add autoincrement

* refactor

* move

* move back

* simplify

* rename

* refactor

* tuple

* comment

* split decryption

* refactor

* rename

* refactor

* move

* remove createSndQueue

* remove comment

* remove JoinInvitationReq

* simplify

* rename

* fix

* type synonim

* getConnData

* simplify

* move

* encoding

* version

* async join with ratchet keys

* fix encoding

* rotate address ratchet keys

* rename, parameter order

* refactor, include ratchet keys into ConnectionRequestUri

* simplify

* remove records

* refactor

* rename, encoding

* clean up

* refactor

* compatible

* addrKeysE2EVersion

* support IKUsePQ for contact addresses

* comments, refactor condition

* typos

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* agent: contact request rejection and service requests (#1833)

* agent: request rejection

* implement RPC requests/responses

* simplify rpc, test, async

* renames

* clean up

* fix

* fix query

* set flag at creation

* move

* command type, schema

* rename

* refactor

* typo

Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

* allow overriding the service request timeout per request

* fixes

* use SSENT event for service replies

* AgentServiceError encoding

* sign service requests

* add bad signature error

* update the plan

* clean up

* refactor

* empty line

Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

* add indices

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>

* update schemas

* tryAllErrors

* combine queries

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: simplex-chat-agent[bot] <287173099+simplex-chat-agent[bot]@users.noreply.github.com>
2026-07-31 19:32:34 +01:00
Evgeny Poberezkin 066a93861b Merge branch 'stable' 2026-07-31 15:21:20 +01:00
Evgeny Poberezkin 27a37387be 7.0.1.0 2026-07-31 15:20:41 +01:00
sh d65d790a20 ntf server: shard push workers per notification token (#1840)
* ntf server: shard push workers per notification token

* ntf server: inline push worker shard calculation
2026-07-31 15:19:43 +01:00
Evgeny c2eb680535 clients, servers: drop support of SMP protocol versions before 01/2025 (#1837)
* servers: drop support of versions before 01/2025

* fix agent tests

* fix ntf tests

* fix test

* remove code for non-batched transmissions

* fix batching test

* fix ntf

* comment
2026-07-31 09:43:19 +01:00
shandEvgeny Poberezkin 7d0820dd44 smp server: do not create messaging queues in SMP proxy to prevent deadlock in processing (#1839)
* smp: fix proxy message queue memory leak

* smp server: do not create messaging queues in SMP proxy to prevent deadlock in processing

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-07-30 16:33:27 +01:00
Evgeny Poberezkin efaad8e734 7.0.0.6 2026-07-25 11:58:17 +01:00
Evgeny 1b4dcfe63e agent: refactor type for private keys in double ratchet (#1830) 2026-07-19 18:27:55 +01:00
Evgeny Poberezkin f7e8ed52bf 7.0.0.5 2026-07-18 06:35:31 +01:00
sh 399c5fe8c6 agent: pass optional SMP server to prepareConnectionLink (#1771) 2026-07-14 09:04:19 +01:00
sh 43e46dd8cc smp-server: fix service subscription memory leak (#1827)
Service subscription counters (totalServiceSubs, serviceSubsCount,
ntfServiceSubsCount) are TVar (Int64, IdsHash). modifyTVar' only forces
the pair to WHNF, so `n +/- n'` and `idsHash <> idsHash'` stay
unevaluated and accumulate an unbounded thunk chain under subscription
and delivery churn (the IdsHash chain also retains a bytestring per
update) - a space leak proportional to the number of updates.

Force both components in addServiceSubs/subtractServiceSubs. Verified
with the load bench: svc churn drops from +5.4 KiB/iter (linear) to flat.
2026-07-10 09:00:40 +01:00
Evgeny Poberezkin 551de8039f 7.0.0.4 2026-07-06 07:34:52 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> a45d764eaa agent: fix rare race conditions in async API (#1792)
* agent: fix rare race conditions in async API

* split async accept too

* fix, reduce diff

* composition

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-07-03 15:24:43 +01:00
Evgeny Poberezkin 836254a4c6 types: rename name types 2026-07-02 12:58:43 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 93925b257c types: instance for contact connection type (#1822)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-07-01 17:37:45 +01:00
Evgeny Poberezkin 6ef38a6ee7 7.0.0.3 2026-06-30 23:24:49 +01:00
shEvgenyEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
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
sh be58967a86 smp-server: fix subscriptions memory leak (#1820)
* docs: plan for service subscriptions memory leak

* fix: remove leaked service delivery subscriptions

The CSAEndServiceSub handler decremented subscription counters but did
not remove the per-queue delivery Sub from the service client's
subscriptions map. Over queue churn a long-lived service connection
accumulated orphaned Sub entries until disconnect, leaking memory.

Mirror CSAEndSub via endServiceQueueSub (reusing endSub) so the entry
is removed and its delivery thread cancelled.

Add a regression test with white-box access to the server Env via
runSMPServerBlocking_; verified failing without the fix.

* test: remove service subs leak regression test

Remove testServiceSubsRemovedOnQueueDelete and the test-only Env
exposure (runSMPServerBlocking_, withSmpServerConfigEnvOn,
serviceSubsMapSize), leaving only the server fix.

* refactor: simplify unsubPrev with applicative

Express the cancel-if-present logic as sequence_ (unsub_ <*> s_).
2026-06-30 15:38:45 +00:00
shandEvgeny Poberezkin c9ebf72e80 smp: fix proxy reconnection to relay after restart (#1806)
* tests: add SMP proxy relay reconnection tests

Reproduces the proxy failing to reconnect to a destination relay when the
sender disconnects mid-connection (empty session var left in smpClients).

* fix: bracket session var creation to drop it on interrupt

getSessVar inserts an empty session var that the connect path then fills with
putTMVar. If the connecting thread is killed by an async exception before that
fill (a proxy worker on client disconnect, an agent worker on cancel), the empty
var was left in the map forever and every later request for that server blocked
on it until timing out (permanent PCEResponseTimeout).

Wrap get-or-create with withGetSessVar (bracketOnError) at the call sites, so the
cleanup is established where the var is created and covers the whole connect: on
interrupt before fill the still-empty var is dropped and the next request
reconnects. This closes the window between getSessVar and the fill that a handler
installed inside the connect function cannot cover.

* test: cover session var leak on interrupted connect

UtilTests: tryAllErrors rethrows ThreadKilled/StackOverflow (the mechanism
that skips putTMVar). SMPProxyTests: agent client reconnection after a
cancelled connect, plus a control proving the stalling relay alone does not
cause the failure; refine the relay reconnection tests.

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-06-29 10:49:00 +00:00
sh 2dff11a808 resolver: cleanup (#1817)
* resolver: cleanup

* resolver: update .testing registry address
2026-06-23 16:30:44 +01:00
Evgeny Poberezkin 98391fd677 7.0.0.2 2026-06-21 13:28:46 +01:00
Evgeny Poberezkin d32a25c988 Merge branch 'stable' 2026-06-21 13:28:16 +01:00
EvgenyandPaul Bottinelli b2bdade380 fix: ignore pending XFTP files in storage accounting (#1814)
* fix: ignore pending XFTP files in storage accounting

* style

---------

Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-21 13:27:28 +01:00
Evgeny Poberezkin 92598c2ddb 6.5.5.0 2026-06-21 13:08:27 +01:00
Samy 84724bc03e crypto: validate BBS proof parameters (#1810) 2026-06-21 13:06:30 +01:00
Paul Bottinelli 91cb297e9e fix: disable web in cloud scripts without certs (#1804) 2026-06-21 12:52:08 +01:00
74a86043cc lib: parse bracketed IPv6 server addresses (#1807)
* Parse bracketed IPv6 server hosts

* lib: parse service-scheme and invitation hosts via TransportHost

* correct encoding

* encoding

---------

Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-06-21 12:18:34 +01:00
Evgeny 958de3bfca library: limit decompressed size (#1815) 2026-06-21 12:11:48 +01:00
EvgenyandPaul Bottinelli 45b21ec1db Reject duplicate STM short link updates (#1813)
Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-21 09:42:35 +01:00
EvgenyEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>Paul Bottinellish
aca1d9a462 crypto: sntrup length validation (#1811)
* Validate SNTRUP761 KEM input lengths

* crypto: BBS scheme for anonymous credentials with multiple presentations (#1794)

* crypto: BBS scheme for anonymous credentials with multiple presentations

* verify

* add files to sources

* more files

* more files, use cabal 3.0

* fix path

* extensions

* switch libbbs to fork

* return either from keygen

* use only secret key to sign

* improve FFI

* simplify

* update libbbs to support iOS

* add commoncrypto flag

* bump libbbs

* reject input of wrong length

* ci: get submodules

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* 6.5.4.0

* core: add getentropy shim for windows build (#1809)

* simplify

---------

Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2026-06-21 08:03:20 +01:00
brenziandsh 056314396d SNRC name resolver (#1795)
* add REST API to resolve SNRC

* fix unset fields

* support multi-TLD deployments

* update for mainnet tests

* haskell-friendly fieldnames

* add subname hint

* resolver: dockerize

* support multiple fallback links for splx contact and channels

* add test

* change url separator to semicolon

---------

Co-authored-by: sh <github.shum@liber.li>
2026-06-20 10:09:56 +01:00
Evgeny Poberezkin df6c53f830 7.0.0.1 2026-06-18 14:33:37 +01:00
Evgeny Poberezkin 220371cec1 Merge branch 'stable' 2026-06-18 14:29:31 +01:00
sh 44898bf7f6 core: add getentropy shim for windows build (#1809) 2026-06-18 14:28:55 +01:00
Evgeny Poberezkin 8e0b8de529 7.0.0.0 2026-06-17 17:14:36 +01:00
shandPaul Bottinelli db3e98f13a ntf-server: add push provider policy (#1808)
* Disable APNS test provider in production

* refactor(ntf): extract guardPushProvider for test-provider guard

* test(ntf): fix APNS test provider test compilation

* ntf server: use ifM for push provider guard (review)

---------

Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-17 09:14:38 +01:00
shandPaul Bottinelli 8a1b5608bf xftp-cli: add deprecation notice (#1799)
Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-16 10:07:13 +01:00
Evgeny Poberezkin e250a9ec9d Merge branch 'stable' 2026-06-16 06:50:14 +01:00
Evgeny Poberezkin 376d6a261a 6.5.4.0 2026-06-15 22:26:45 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 9f9b6c8e88 crypto: BBS scheme for anonymous credentials with multiple presentations (#1794)
* crypto: BBS scheme for anonymous credentials with multiple presentations

* verify

* add files to sources

* more files

* more files, use cabal 3.0

* fix path

* extensions

* switch libbbs to fork

* return either from keygen

* use only secret key to sign

* improve FFI

* simplify

* update libbbs to support iOS

* add commoncrypto flag

* bump libbbs

* reject input of wrong length

* ci: get submodules

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-06-15 09:44:11 +01:00
shandPaul Bottinelli 24e464926e scripts: fix check in simplex-servers-update (#1797)
Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-06 09:05:24 +01:00
shandPaul Bottinelli 7d3cfa56d3 xftp-web: remove debug logs (#1798)
Co-authored-by: Paul Bottinelli <paul.bottinelli@trailofbits.com>
2026-06-06 09:04:26 +01:00
sh 53bc0fe663 scripts: add docker-compose resolver setup (#1793) 2026-06-02 10:24:56 +01:00
Evgeny Poberezkin b981dcb70b 6.5.3.0 2026-06-01 13:17:47 +01:00
Evgeny Poberezkin 61ee188ee0 Merge branch 'stable' 2026-05-30 13:23:26 +01:00
Evgeny 39eb3c4a13 smp: fix handshake for rcv services between new client & old server (#1790) 2026-05-29 09:31:37 +01:00
sh ee2ff402fe agent: split SimplexNameDomain out of SimplexNameInfo (#1788)
* agent: split SimplexNameDomain out of SimplexNameInfo

The type now separates the user-supplied type prefix (#/@) from the
domain itself:

  data SimplexNameInfo = SimplexNameInfo
    { nameType :: SimplexNameType
    , nameDomain :: SimplexNameDomain
    }

  data SimplexNameDomain = SimplexNameDomain
    { nameTLD :: SimplexTLD
    , domain :: Text
    , subDomain :: [Text]
    }

The domain is independent of the contact-vs-public-group distinction —
the same dotted-labels structure applies to both. Future code that
needs to talk about a domain without committing to a name type (e.g.
server-side TLD-based registry lookup) can use SimplexNameDomain
directly.

fullDomainName now operates on SimplexNameDomain rather than the
full info wrapper. Parser, StrEncoding instance, and aeson derivations
updated accordingly. No external callers needed updating.

* agent: split StrEncoding instance for SimplexNameDomain

* agent: flatten TLD case + use unless guard

* agent: address review - strict domain parser, permissive channel
2026-05-29 09:11:08 +01:00
Evgeny Poberezkin 04960864c4 Merge branch 'stable' 2026-05-28 09:33:18 +01:00
Evgeny Poberezkin e9265a7f7c agent: allow all-digit names 2026-05-27 18:06:44 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 7682999505 agent: types for namespace support (#1786)
* agent: types for namespace support

* parser

* refactor

* more refactor

* simplify

* refactor again

* refactor

* refactor

* import

* use @ for contact addresses

* remove AConnectTarget

* update parser and types

* revert TLDWeb

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-05-27 13:05:13 +01:00
Evgenyspaced4ndyEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>sh
f0b7a4be73 messaging services (#1667)
* smp server: messaging services (#1565)

* smp server: refactor message delivery to always respond SOK to subscriptions

* refactor ntf subscribe

* cancel subscription thread and reduce service subscription count when queue is deleted

* subscribe rcv service, deliver sent messages to subscribed service

* subscribe rcv service to messages (TODO delivery on subscription)

* WIP

* efficient initial delivery of messages to subscribed service

* test: delivery to client with service certificate

* test: upgrade/downgrade to/from service subscriptions

* remove service association from agent API, add per-user flag to use the service

* agent client (WIP)

* service certificates in the client

* rfc about drift detection, and SALL to mark end of message delivery

* fix test

* fix test

* add function for postgresql message storage

* update migration

* servers: maintain xor-hash of all associated queue IDs in PostgreSQL (#1668)

* servers: maintain xor-hash of all associated queue IDs in PostgreSQL (#1615)

* ntf server: maintain xor-hash of all associated queue IDs via PostgreSQL triggers

* smp server: xor hash with triggers

* fix sql and using pgcrypto extension in tests

* track counts and hashes in smp/ntf servers via triggers, smp server stats for service subscription, update SMP protocol to pass expected count and hash in SSUB/NSSUB commands

* agent migrations with functions/triggers

* remove agent triggers

* try tracking service subs in the agent (WIP, does not compile)

* Revert "try tracking service subs in the agent (WIP, does not compile)"

This reverts commit 59e908100d.

* comment

* agent database triggers

* service subscriptions in the client

* test / fix client services

* update schema

* fix postgres migration

* update schema

* move schema test to the end

* use static function with SQLite to avoid dynamic wrapper

* agent: fail when per-connection transport isolation is used with services (#1670)

* agent: service subscription events (#1671)

* agent: use server keyhash when loading service record

* agent: process queue/service associations with delayed subscription results

* agent: service subscription events

* agent: finalize initial service subscriptions, remove associations on service ID changes (#1672)

* agent: remove service/queue associations when service ID changes

* agent: check that service ID in NEW response matches session ID in transport session

* agent subscription WIP

* test

* comment

* enable tests

* update queries

* agent: option to add SQLite aggregates to DB connection  (#1673)

* agent: add build_relations_vector function to sqlite

* update aggregate

* use static aggregate

* remove relations

---------

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

* add test, treat BAD_SERVICE as temp error, only remove queue associations on service errors

* add packZipWith for backward compatibility with GHC 8.10.7

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* servers: service stats and logging, allow services without option (removed), report errors during service message delivery, remove threads when service subscription ended (#1676)

* smp server: always allow services without option

* smp server: maintain IDs hash in session subscription states

* smp server: service message delivery error handling

* ntf server: log subscription count and hash differences

* smp server: remove delivery threads when service subscription ended/client disconnected

* agent: remove service queue association when service ID changed, process ENDS event, test migrating to/from service (#1677)

* agent: remove service queue association when service ID changed

* agent: process ENDS event

* agent: send service subscription error event

* agent: test migrating to/from service subscriptions, fixes

* agent: always remove service when disabled, fix service subscriptions

* ntf server: use different client certs for each SMP server, remove support for store log (#1681)

* ntf server: remove support for store log

* ntf server: use different client certificates for each SMP server

* smp protocol: fix encoding for SOKS/ENDS responses (#1683)

* agent: create user with option to enable client service (#1684)

* agent: create user with option to enable client service

* handle HTTP2 errors

* do not catch async exceptions

* agent: minor fixes

* docs: update protocol (#1705)

* docs: agent threat model

* update protocol docs

* update RFCs (#1730)

* update RFCs

* update

* update overview

* update terminology

* original language in threat model

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* docs: fix minor issues in protocols

* docs: add e2e encrypted message wire encoding to PQDR spec

* docs: add missing encodings and other protocol corrections

* docs: move implemented rfcs

* smp: service fixes (#1737)

* smp: deliver service subscription to correct client

* tests: more resilient to concurrency

* optimize PostgreSQL query

* fix service re-association after server "downgrade"

* correctly handle service removed from server (and ID changed)

* remove unused

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* prometheus: fix metrics names (#1747)

* test: rcv service re-association on restart (#1746)

* agent: correct log message

* docs: update whitepaper

* smp: fix messaging client service issues (#1751)

* services: fix minor issues

* fix accounting for subscribed service queues, add prometheus stats

* fix uncorrelated subquery

* fix potential race condition when inserting service defensively, as it is also prevented by how client is created

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* agent: refactor cleanup if no pending subs (#1757)

* smp server: batch processing of subscription messages (#1753)

* smp server: batch processing of subscription messages

* refactor

* empty line

* fix

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* smp: batch queue association updates on subscriptions (#1760)

* smp: batch queue association updates on subscriptions

* refactor to fused batching

* simpler

* batch assoc functions

* clean up

* fix

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>

* agent: use primary key index in setRcvServiceAssocs (#1783)

* agent: use primary key index in setRcvServiceAssocs

Previous WHERE rcv_id = ? did not match the (host, port, rcv_id)
primary key prefix and fell back to a table scan via
idx_rcv_queues_client_notice_id. With ~390k rows per queue, each
update in a 1350-row batch scanned the whole table, yielding ~290s
per batch and a multi-hour rcv-services migration.

* agent: pass SMPServer explicitly to setRcvServiceAssocs

Avoid extracting host/port from the first queue inside setRcvServiceAssocs.
The caller already has SMPServer in scope (from tSess) and the call chain
is short, so threading it through is simpler than inspecting the list.
Removes the empty-list guard from setRcvServiceAssocs (it remains in
processRcvServiceAssocs).

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2026-05-21 14:14:03 +01:00
Evgeny Poberezkin f03cec7a58 6.5.2.0 2026-05-21 09:10:25 +01:00
sh b6f551000f ntf server: concurrent APNS push via sendRequestDirect (#1780)
The per-(srvHost, provider) worker shards added in #1779 still funnel
all APNS sends through one HTTP2Client's reqQ, where a single process
thread calls sendRequest serially - one in-flight HTTP/2 stream at a
time, capping APNS throughput at 1/RTT.

sendRequestDirect bypasses the queue and invokes sendReq directly from
the calling worker, so concurrent workers open parallel HTTP/2 streams
on the shared APNS connection and the multiplexing happens on the wire.
2026-05-18 14:35:47 +01:00
shEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>Evgeny Poberezkin
012c8cc104 ntf server: concurrent notification delivery (#1779)
* ntf-server: carry retry reason in PPRetryLater, log retries

Change PPRetryLater from nullary to PPRetryLater Text so the cause
(503 / 410-reason) propagates to the retry call site. Log a warning
at every retry attempt with provider, token id and reason.

* ntf-server: parallel push delivery via forkIO + per-srvHost lock

Fork delivery per notification, taking an MVar keyed by srvHost_ so
notifications from the same SMP server serialize while different
servers proceed concurrently. Switch APNS to sendRequestDirect so
concurrent deliveries share one HTTP/2 connection via stream
multiplexing rather than serializing through the client reqQ.

* ntf-server: single-flight push client creation via SessionVar

Match the take/create/wait pattern in Agent/Client.hs
(newProtocolClient / waitForProtocolClient). pushClients now wraps
clients in SessionVar (Either SomeException PushProviderClient) so
concurrent first-time access and concurrent retries collapse to a
single mkClient call; waiters observe the winner's result via
readTMVar (or its error). retryDeliver evicts the failing client by
SessionVar identity before re-fetching.

* use multiple queues and workers, remove semaphores and threads per notification

* fix

* retry connecting client

* fix

* move config

* fix

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-05-18 09:26:50 +01:00
sh fd298ae328 xftp: web page tweaks (#1772) 2026-05-06 09:24:18 +01:00
Evgeny Poberezkin 1f173abf6d 6.5.1.0 2026-05-01 17:57:19 +01:00
Evgeny 21f4597dad xftp: backwards compatible file header decoding (#1768) 2026-05-01 13:32:19 +01:00
Evgeny Poberezkin ba6af65c54 6.5.0.17 2026-04-29 20:31:42 +01:00
Evgeny Poberezkin 858fac7f4f 6.5.0.16 2026-04-21 20:26:51 +01:00
Evgeny Poberezkin 90432a44b4 tests: fix test compilation 2026-04-20 13:42:25 +01:00
sh 1e1f897c79 core: use = as INI key-value separator (#1767)
* core: use = as INI key-value separator

* core: update docker entrypoints for = INI separator

* core: update INI separator in README and test scripts
2026-04-20 09:22:14 +01:00
sh 0dc2940eff ci: add xftp-server postgres binaries (#1766) 2026-04-17 15:57:30 +01:00
sh 8833e5c1b5 xftp-server: support postgresql backend (#1755)
* xftp: add PostgreSQL backend design spec

* update doc

* adjust styling

* add implementation plan

* refactor: move usedStorage from FileStore to XFTPEnv

* refactor: add getUsedStorage, getFileCount, expiredFiles store functions

* refactor: change file store operations from STM to IO

* refactor: extract FileStoreClass typeclass, move STM impl to Store.STM

* refactor: make XFTPEnv and server polymorphic over FileStoreClass

* feat: add PostgreSQL store skeleton with schema migration

* feat: implement PostgresFileStore operations

* feat: add PostgreSQL INI config, store dispatch, startup validation

* feat: add database import/export CLI commands

* test: add PostgreSQL backend tests

* fix: map ForeignKeyViolation to AUTH in addRecipient

When a file is concurrently deleted while addRecipient runs, the FK
constraint on recipients.sender_id raises ForeignKeyViolation. Previously
this propagated as INTERNAL; now it returns AUTH (file not found).

* fix: only decrement usedStorage for uploaded files on expiration

expireServerFiles unconditionally subtracted file_size from usedStorage
for every expired file, including files that were never uploaded (no
file_path). Since reserve only increments usedStorage during upload,
expiring never-uploaded files caused usedStorage to drift negative.

* fix: handle setFilePath error in receiveServerFile

setFilePath result was discarded with void. If it failed (file deleted
concurrently, or double-upload where file_path IS NULL guard rejected
the second write), the server still reported FROk, incremented stats,
and left usedStorage permanently inflated. Now the error is checked:
on failure, reserved storage is released and AUTH is returned.

* fix: escape double quotes in COPY CSV status field

The status field (e.g. "blocked,reason=spam,notice={...}") is quoted in
CSV for COPY protocol, but embedded double quotes from BlockingInfo
notice (JSON) were not escaped. This could break CSV parsing during
import. Now double quotes are escaped as "" per CSV spec.

* fix: reject upload to blocked file in Postgres setFilePath

In Postgres mode, getFile returns a snapshot TVar for fileStatus. If a
file is blocked between getFile and setFilePath, the stale status check
passes but the upload should be rejected. Added status = 'active' to
the UPDATE WHERE clause so blocked files cannot receive uploads.

* fix: add CHECK constraint on file_size > 0

Prevents negative or zero file_size values at the database level.
Without this, corrupted data from import or direct DB access could
cause incorrect storage accounting (getUsedStorage sums file_size,
and expiredFiles casts to Word32 which wraps negative values).

* fix: check for existing data before database import

importFileStore now checks if the target database already contains
files and aborts with an error. Previously, importing into a non-empty
database would fail mid-COPY on duplicate primary keys, leaving the
database in a partially imported state.

* fix: clean up disk file when setFilePath fails in receiveServerFile

When setFilePath fails (file deleted or blocked concurrently, or
duplicate upload), the uploaded file was left orphaned on disk with
no DB record pointing to it. Now the file is removed on failure,
matching the cleanup in the receiveChunk error path.

* fix: check storeAction result in deleteOrBlockServerFile_

The store action result (deleteFile/blockFile) was discarded with void.
If the DB row was already deleted by a concurrent operation, the
function still decremented usedStorage, causing drift. Now the error
propagates via ExceptT, skipping the usedStorage adjustment.

* fix: check deleteFile result in expireServerFiles

deleteFile result was discarded with void. If a concurrent delete
already removed the file, deleteFile returned AUTH but usedStorage
was still decremented — causing double-decrement drift. Now the
usedStorage adjustment and filesExpired stat only run on success.

* refactor: merge STM store into Store.hs, parameterize server tests

- Move STMFileStore and its FileStoreClass instance from Store/STM.hs
  back into Store.hs — the separate file was unnecessary indirection
  for the always-present default implementation.

- Parameterize xftpFileTests over store backend using HSpec SpecWith
  pattern (following SMP's serverTests approach). The same 11 tests
  now run against both memory and PostgreSQL backends via a bracket
  parameter, eliminating all *Pg test duplicates.

- Extract shared run* functions (runTestFileChunkDeliveryAddRecipients,
  runTestWrongChunkSize, runTestFileChunkExpiration, runTestFileStorageQuota)
  from inlined test bodies.

* refactor: clean up per good-code review

- Remove internal helpers from Postgres.hs export list (withDB, withDB',
  handleDuplicate, assertUpdated, withLog are not imported externally)
- Replace local isNothing_ with Data.Maybe.isNothing in Env.hs
- Consolidate duplicate/unused imports in XFTPStoreTests.hs
- Add file_path IS NULL and status guards to STM setFilePath, matching
  the Postgres implementation semantics

* test: parameterize XFTP server, agent and CLI tests over store backend

- xftpTest/xftpTest2/xftpTest4/xftpTestN now take XFTPTestBracket as
  first argument, enabling the same test to run against both memory
  and PostgreSQL backends.

- xftpFileTests (server tests), xftpAgentFileTests (agent tests), and
  xftpCLIFileTests (CLI tests) are SpecWith-parameterized suites that
  receive the bracket from HSpec's before combinator.

- Test.hs runs each parameterized suite twice: once with
  xftpMemoryBracket, once with xftpPostgresBracket (CPP-guarded).

- STM-specific tests (store log restore/replay) stay in memory-only
  xftpAgentTests. SNI/CORS tests stay in memory-only xftpServerTests.

* refactor: remove dead test wrappers after parameterization

Remove old non-parameterized test wrapper functions that were
superseded by the store-backend-parameterized test suites.
All test bodies (run* and _ functions) are preserved and called
from the parameterized specs. Clean up unused imports.

* feat: add manual tests and guide

* refactor: merge file_size CHECK into initial migration

* refactor: extract rowToFileRec shared by getFile sender/recipient paths

* refactor: parameterize XFTPServerConfig over store type

Embed XFTPStoreConfig s as serverStoreCfg field, matching SMP's
ServerConfig. runXFTPServer and newXFTPServerEnv now take a single
XFTPServerConfig s. Restore verifyCmd local helper structure.

* refactor: minimize diff in tests

Restore xftpServerTests and xftpAgentTests bodies to match master
byte-for-byte (only type signatures change for XFTPTestBracket
parameterization); inline the runTestXXX helpers that were split
on this branch.

* refactor: restore getFile position to match master

* refactor: rename withSTMFile back to withFile

* refactor: close store log inside closeFileStore for STM backend

Move STM store log close responsibility into closeFileStore to
match PostgresFileStore, removing the asymmetry where only PG's
close was self-contained.

STMFileStore holds the log in a TVar populated by newXFTPServerEnv
after readWriteFileStore; stopServer no longer needs the explicit
withFileLog closeStoreLog call. Writes still go through XFTPEnv.storeLog
via withFileLog (unchanged).

* refactor: rename XFTPTestBracket to XFTPTestServer

* fix: move file_size check from PG schema to store log import

* refactor: use SQL-standard type names in XFTP schema

* perf: batch expired file deletions with deleteFiles

* refactor: stream export instead of loading recipients into memory

* refactor: parameterize XFTP store with FSType singleton dispatch

* refactor: minimize diff per review feedback

* refactor: use types over strings, deduplicate parser

* refactor: always parse database store type, fail at startup

* fix compilation without postgresql

* refactor: always parse database store type, fail at startup
2026-04-16 09:06:04 +01:00
Evgeny 95b17ada27 lib: fix incorrect encoding of Signature (incompatible with decoding, but never used together) - breaks backward compatibility for remote control connections (#1765)
* lib: fix incorrect StrEncoding of Signature (it was not compatible with decoding, but was never used)

* align encoding with used in links (breaks backward compatibility)
2026-04-15 15:11:06 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 43cdf55f3b lib: add JSON instance to Signature type (#1764)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-04-14 19:47:20 +01:00
Evgeny Poberezkin bc5ea42bec 6.5.0.15 2026-04-11 19:39:50 +01:00
Evgeny Poberezkin 0933cbcb9c agent: add compression api 2026-04-11 18:14:48 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> f2dafd983b agent: export decompressedSize (#1763)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-04-11 17:28:00 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 34c0909c1a agent: drop message after N reception attempts (#1762)
* agent: drop message after N reception attempts

* test

* increase count for message expiration

* fix migration

* update schema

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-04-11 16:24:30 +01:00
Evgeny Poberezkin 97802a30fc 6.5.0.14 2026-04-04 17:28:23 +01:00
Evgeny b82cf7d001 xftp: remove page (#1761) 2026-04-03 10:47:52 +01:00
spaced4ndy 9bc0c70fa0 agent: getConnLinkPrivKey (#1759) 2026-04-02 15:22:44 +00:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 0741583f78 agent: read queues in batches for subscriptions (#1758)
* agent: read queues in batches for subscriptions

* resubscribe in batches too

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-04-01 16:07:17 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> f8f172f32f agent: fix race when pending subscriptions are never subscribed (#1756)
* agent: fix race when pending subscriptions are never subscribed

* small agent

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-03-31 19:16:54 +01:00
spaced4ndy 9c07ddff3c agent: allow to use existing connId for getConnShortLinkAsync (#1752) 2026-03-30 09:48:31 +00:00
Evgeny Poberezkin 50b71d3e56 6.5.0.12 2026-03-29 07:54:48 +01:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> a1b762992b agent: pass key and link ID when preparing group link (#1754)
* agent: pass key and link ID when preparing group link

* binding

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-03-28 20:29:48 +00:00
Evgeny Poberezkin 1e0093be9a docs: update whitepaper 2026-03-26 20:48:47 +00:00
sh 1a12ee0a5a xftp-web: version bump to 0.3.0 (#1742) 2026-03-20 11:43:43 +00:00
sh efcef2d1fd xftp-web: add postgres schema cleanup for integration tests (#1741)
Stale postgres schema leaked pending XFTP operations between
cross-language tests, causing N-1 of N tests to fail.
2026-03-20 09:58:56 +00:00
Evgeny 963d7b2f75 fix small bugs (#1740)
* fix small bugs

* toChunks
2026-03-20 08:59:38 +00:00
Evgeny Poberezkin 4e4e0a4f42 6.5.0.11 2026-03-17 09:26:02 +00:00
sh 082a6c6f22 web: serve on-the-fly compressed gzip static files (#1735)
* web: serve pre-compressed gzip static files

* web: compress static files on the fly instead of pre-compressed
2026-03-16 09:08:43 +00:00
shandEvgeny dc2921e4ce xftp-server: embed file download widget in XFTP server web page (#1733)
* xftp-server: embed file download widget in XFTP server web page

When a URL has a hash fragment (>50 chars), the server page shows the
file download UI instead of the server info page. Embeds xftp-web
assets (JS, CSS, crypto worker) and protocol overlay with matching
website content. Overlay renders below the server navbar.

* xftp-server: fix overlay scroll lock, remove extra margin, fix dark SVG

* xftp-server: move file transfer widget to standalone /file page

* web: collapse all repeated Nothing sections in render

section_ only collapsed the first occurrence of a section when content
was Nothing, leaving subsequent sections with the same label intact.
This caused SMP server pages to show raw <x-xftpConfig> tags.

* xftp-server: update bundled css/js

* xftp-server: move file.html to xftp-server, rename xftp bundle dir

* web: remove unused server-info wrapper div

* refactor

* fix

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2026-03-13 16:00:02 +00:00
sh 5a32e729e0 xftp-web: add "Upload your file" link after download completes (#1736)
* xftp-web: add "Upload your file" link after download completes

* xftp-web: call initUpload directly instead of hashchange dispatch
2026-03-13 15:01:15 +00:00
shandEvgeny Poberezkin 782cacfb3c fix: using simplexmq as dependency (move embedFile to executables) (#1734)
* web: parameterize generateSite, remove Embedded from library

Move embedFile/embedDir out of the library so it works when
simplexmq is consumed as a dependency. generateSite now accepts
mediaContent, wellKnown, and linkHtml as parameters.

* smp-server, xftp-server: embed static files in executables

Add shared apps/common/Embedded.hs with TH splices, update SMPWeb
and XFTPWeb to pass embedded content to generateSite, move
file-embed dependency from library to executables and test suite.

* refactor

* add export, move common files to Web subfolder

* fix .cabal

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-03-12 17:05:00 +00:00
sh 328d3b941a xftp-web: use XFTP server domain in share link, verify on download (#1732)
* xftp-web: use XFTP server domain in share link, verify on download

Use a random XFTP server host from the file description as the link
origin instead of the current page domain. On download, verify
that the hosting domain matches one of the servers in the description.

* xftp-web: use first description server as link origin

* xftp-web: show wrong server error in download UI instead of blank page
2026-03-11 15:59:26 +00:00
sh b6b87a6323 xftp-web: disable minification in vite build (#1731) 2026-03-11 09:08:23 +00:00
sh 33454444a4 xftp-web: new version (#1728)
* xftp-web: remove flux from preset

* xftp-web: new version
2026-03-09 14:26:40 +00:00
Evgeny Poberezkin 1449e6418c 6.5.0.10 2026-03-09 12:25:08 +00:00
sh 3a627a9599 tests: use correct web CA certificate in CLI static files test (#1727) 2026-03-09 12:23:03 +00:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> d2d834ad16 agent: validate destination relay certificate, allow 3-4 certificate chains (#1717)
Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-03-09 12:22:42 +00:00
shandEvgeny Poberezkin 437cdde4a5 xftp: add web page for server information (#1724)
* xftp: add web page for server information

* web: rename XFTP.Web to XFTPWeb, remove XFTP subdirectory

* refactor(xftp): remove storage quota from web page

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-03-09 08:44:28 +00:00
shandEvgeny Poberezkin eed1bf14c6 web: extract shared web module from smp-server (#1723)
* web: extract shared web module from smp-server

Move web serving infrastructure (warp, static files, HTML templating)
from apps/smp-server/web/Static.hs into library modules:
- Simplex.Messaging.Server.Web (generic web infra + templating)
- Simplex.Messaging.Server.Web.Embedded (TH-embedded assets)

Move static assets from apps/smp-server/static/ to
src/Simplex/Messaging/Server/Web/.

Move EmbeddedWebParams/WebHttpsParams from Server.Main to Server.Web.

Keep SMP-specific rendering (serverInformation) in apps/smp-server/SMP/Web.hs.

generateSite is now generic: takes pre-rendered HTML + link page paths,
enabling reuse by XFTP and NTF servers.

* web: add tests for templating engine

Tests for render, section_, item_, and timedTTLText functions
in Simplex.Messaging.Server.Web module.

* web: add serverInfoSubsts, serveStaticPageH2, safe port parsing

* web: rename SMP.Web to SMPWeb, remove SMP subdirectory

* fix(web): section_ collapsing sections with Just "" content

Commit e48bedea ("servers: fix server pages when source code is not
specified") changed section_ to treat Just "" the same as Nothing -
collapsing the section. The intent was to handle the sourceCode case
(empty string when not specified), but the guard
`not (B.null content)` also broke operator, admin, complaints, and
hosting - all of which legitimately use Just "" as a
section-present marker.

Before (correct):
  Nothing -> before <> next
  Just content -> before <> item_ label content inside <> ...

After (broken):
  Just content | not (B.null content) -> ...
  _ -> before <> next

Restore the original behavior: only Nothing collapses a section.

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-03-09 08:42:38 +00:00
Evgeny @ SimpleX Chat 313e96513c docs: correction to governance process (#1725) 2026-03-08 17:27:56 +00:00
Evgeny c6e3a4d80f add missing exports (#1722)
* add missing exports

* fix dependency
2026-03-04 07:31:46 +00:00
Evgeny Poberezkin 0e67647e90 6.5.0.9 2026-03-02 18:14:37 +00:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> f3408d9bb6 explicit exports (#1719)
* explicit exports

* more empty exports

* add exports

* reorder

* use correct ControlProtocol type for xftp router

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-03-02 17:34:01 +00:00
sh 3ecb901e3e xftp-web: fix build and Playwright test failures (#1720) 2026-03-02 16:22:36 +00:00
EvgenyEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>shumsh
f6aca47604 xftp: implementation of XFTP client as web page (#1708)
* xftp: implementation of XFTP client as web page (rfc, low level functions)

* protocol, file descriptions, more cryptogrpahy, handshake encoding, etc.

* xftp server changes to support web slients: SNI-based certificate choice, CORS headers, OPTIONS request

* web handshake

* test for xftp web handshake

* xftp-web client functions, fix transmission encoding

* support description "redirect" in agent.ts and cross-platform compatibility tests (Haskell <> TypeScript)

* rfc: web transport

* client transport abstraction

* browser environment

* persistent client sessions

* move rfcs

* web page plan

* improve plan

* webpage implementation (not tested)

* fix test

* fix test 2

* fix test 3

* fixes and page test plan

* allow sending xftp client hello after handshake - for web clients that dont know if established connection exists

* page tests pass

* concurrent and padded hellos in the server

* update TS client to pad hellos

* fix tests

* preview:local

* local preview over https

* fixed https in the test page

* web test cert fixtures

* debug logging in web page and server

* remove debug logging in server/browser, run preview xftp server via cabal run to ensure the latest code is used

* debug logging for page sessions

* add plan

* improve error handling, handle browser reconnections/re-handshake

* fix

* debugging

* opfs fallback

* delete test screenshot

* xftp CLI to support link

* fix encoding for XFTPServerHandshake

* support redirect file descriptions in xftp CLI receive

* refactor CLI redirect

* xftp-web: fixes and multi-server upload (#1714)

* fix: await sodium.ready in crypto/keys.ts (+ digest.ts StateAddress cast)

* multi-server parallel upload, remove pickRandomServer

* fix worker message race: wait for ready signal before posting messages

* suppress vite build warnings: emptyOutDir, externals, chunkSizeWarningLimit

* fix Haskell web tests: use agent+server API, wrap server in array, suppress debug logs

* remove dead APIs: un-export connectXFTP, delete closeXFTP

* fix TypeScript errors in check:web (#1716)

- client.ts: cast globalThis.process to any for browser tsconfig,
  suppress node:http2 import, use any for Buffer/chunks, cast fetch body
- crypto.worker.ts: cast sha512_init() return to StateAddress

* fix: serialize worker message processing to prevent OPFS handle race

async onmessage allows interleaved execution at await points.
When downloadFileRaw fetches chunks from multiple servers in parallel,
concurrent handleDecryptAndStore calls both see downloadWriteHandle
as null and race on createSyncAccessHandle for the same file,
causing intermittent NoModificationAllowedError.

Chain message handlers on a promise queue so each runs to completion
before the next starts.

* xftp-web: prepare for npm publishing (#1715)

* prepare package.json for npm publishing

Remove private flag, add description/license/repository/publishConfig,
rename postinstall to pretest, add prepublishOnly, set files and main.

* stable output filenames in production build

* fix repository url format, expand files array

* embeddable component: scoped CSS, dark mode, i18n, events, share

- worker output to assets/ for single-directory deployment
- scoped all CSS under #app, removed global resets
- dark mode via .dark ancestor class
- progress ring reads colors from CSS custom properties
- i18n via window.__XFTP_I18N__ with t() helper
- configurable mount element via data-xftp-app attribute
- optional hashchange listener (data-no-hashchange)
- completion events: xftp:upload-complete, xftp:download-complete
- enhanced file-too-large error mentioning SimpleX app
- native share button via navigator.share

* deferred init and runtime server configuration

- data-defer-init attribute skips auto-initialization
- window.__XFTP_SERVERS__ overrides baked-in server list

* use relative base path for relocatable build output

* xftp-web: retry resets to default state, use innerHTML for errors

* xftp-web: only enter download mode for valid XFTP URIs in hash

* xftp-web: render UI before WASM is ready

Move sodium.ready await after UI initialization so the upload/download
interface appears instantly. WASM is only needed when user triggers
an actual upload or download. Dispatch xftp:ready event once WASM loads.

* xftp-web: CLS placeholder HTML and embedder CSS selectors

Add placeholder HTML to index.html so the page renders a styled card
before JS executes, preventing layout shift. Use a <template> element
with an inline script to swap to the download placeholder when the URL
hash indicates a file download. Auto-compute CSP SHA-256 hashes for
inline scripts in the vite build plugin.

Change all CSS selectors from #app to :is(#app, [data-xftp-app]) so
styles apply when the widget is embedded with data-xftp-app attribute.

* xftp-web: progress ring overhaul

Rewrite progress ring with smooth lerp animation, green checkmark on
completion, theme reactivity via MutationObserver, and per-phase color
variables (encrypt/upload/download/decrypt).

Show honest per-phase progress: each phase animates 0-100% independently
with a ring color change between phases. Add decrypt progress callback
from the web worker so the decryption phase tracks real chunk processing
instead of showing an indeterminate spinner.

Snap immediately on phase reset (0) and completion (1) to avoid
lingering partial progress. Clean up animation and observers via
destroy() in finally blocks.

* xftp-web: single progress ring for upload, simplify ring color

* xftp-web: single progress ring for download

* feat(xftp-web): granular progress for encrypt/decrypt phases

Add byte-level progress callbacks to encryptFile, decryptChunks,
and sha512Streaming by processing data in 256KB segments. Worker
reports fine-grained progress across all phases (encrypt+hash+write
for upload, read+hash+decrypt for download). Progress ring gains
fillTo method for smooth ease-out animation during minimum display
delays. Encrypt/decrypt phases fill their weighted regions (0-15%
and 85-99%) with real callbacks, with fillTo covering remaining
time when work finishes under the 1s minimum for files >= 100KB.

* rename package

---------

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

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
Co-authored-by: shum <github.shum@liber.li>
Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2026-03-02 09:57:46 +00:00
Evgeny Poberezkin 50ae1e1c3e docs: update governance process 2026-02-24 18:05:30 +00:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 483ac674fb agent: fix possible deadlocks of queue overloading when processing messages (#1713)
* agent: fix possibly deadlocks of queue overloading when processing messages

* simplify

* refactor

* refactor

* refactor 2

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-02-24 16:47:00 +00:00
EvgenyandEvgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com> 3dba9c2b1e rfc: SimpleX Network Consortium governance (#1718)
* rfc: SimpleX Network Consortium governance

* update

* add draft label

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
2026-02-22 18:58:09 +00:00
Evgeny Poberezkin 8fdc0703bc 6.5.0.8 2026-01-28 23:12:19 +00:00
Evgeny Poberezkin d7b90b8415 Merge branch 'stable' 2026-01-28 22:52:33 +00:00
Evgeny Poberezkin 9346b85c3f 6.4.8.0 2026-01-28 21:58:32 +00:00
Evgeny 3c5ec8d9a1 agent: improve error handling (#1707)
* agent: improve error handling

* simplify

* report critical error when subscriber crashes

* fix test
2026-01-28 21:54:41 +00:00
Evgeny d10e05b796 agent: split creating connection to two steps to prepare connection link in advance (#1704)
* agent: split creating connection to two steps to prepare connection link in advance

* linkEntityId, newOwnerAuth

* simplify
2026-01-27 10:54:13 +00:00
Evgeny 66cc06738e agent: fix tests for short links (#1703) 2026-01-26 00:11:50 +00:00
Evgeny Poberezkin 0f3b8a4a16 docs: update contributing 2026-01-24 18:05:01 +00:00
Evgeny a1596ed234 docs: contributing guide (#1702) 2026-01-24 13:33:17 +00:00
spaced4ndyandEvgeny Poberezkin 89b81d151f agent: getConnShortLinkAsync; implement joinConnectionAsync for contact connections; narrow setConnShortLinkAsync only to Contact connections (#1694)
* agent: getConnShortLinkAsync

* enable all tests

* comment

* comment

* join conn async for contact URI (wip)

* fix test

* remove enableNtfs param

* FixedLinkData

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-01-20 08:40:35 +00:00
Evgeny 3e5b654109 agent: support multiple link owners in link data (#1701)
* agent: support multiple link owners in link data

* fix
2026-01-19 22:08:11 +00:00
spaced4ndyandEvgeny Poberezkin ca26c69937 db: withSavepoint; agent: correctly handle errors in createWithRandomId for postgres (#1693)
* db: withSavepoint; agent: correctly handle errors in createWithRandomId

* comment

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2026-01-15 14:59:45 +00:00
Evgeny Poberezkin 58212c421a 6.5.0.7 2026-01-13 20:55:38 +00:00
Evgenyandspaced4ndy 1000107259 agent: clean up old records in batches (#1692)
* agent: clean up old records in batches

* update

* update

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* stabilize tests

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2026-01-13 19:11:46 +00:00
Evgeny 6aadcf1f3f agent: lock rows for concurrent queries in PostgreSQL (#1688)
* agent: lock rows for concurrent queries in PostgreSQL

* fix race conditions in workers

* refactor
2026-01-08 11:09:58 +00:00
spaced4ndy 07604a146f agent: async command to set connection short link (setConnShortLinkAsync) (#1680) 2026-01-07 15:12:52 +00:00
Ritiek Malhotra 4c782d3191 docker: build multi-arch images for x86-64 and arm64 (#1540) 2026-01-07 10:49:21 +00:00
Evgeny c4b687ba64 agent: reset schema after changes (#1690) 2026-01-05 20:37:50 +00:00
Evgeny a7b43b1a3e agent: use strict tables (#1686)
* agent: use strict tables

* migrate existing tables to strict

* test: verify that all tables are strict

* fix column types for device_token and ntf_mode

* fix encodings and column types for ntf_sub_action and ntf_sub_smp_action

* update schema

* remove debug.trace

* log
2026-01-03 17:19:18 +00:00
Evgeny d6df769799 ntf server: additional tests (#1687) 2026-01-02 14:07:13 +00:00
Evgeny Poberezkin 5f73d1e629 6.5.0.6 2025-12-23 18:00:26 +00:00
Evgeny Poberezkin 70d1b99fb4 Merge branch 'stable' 2025-12-23 14:18:07 +00:00
Evgeny Poberezkin 2ea98db9d8 6.4.7.0 2025-12-23 12:42:46 +00:00
Evgeny bbe1c716e6 xrcp: treat any 127.x.x.x IP address as local (#1682) 2025-12-23 12:42:05 +00:00
Evgeny Poberezkin 77ac452190 6.5.0.5 2025-12-22 21:56:25 +00:00
Evgeny ea70575275 agent: use server cert hash when selecting server queues to subscribe (#1679) 2025-12-15 11:48:17 +00:00
Evgeny 49e9ce1649 agent: skip subscribing servers without queues (#1678) 2025-12-14 23:01:21 +00:00
Evgeny Poberezkin 2ca440dd2d 6.5.0.4 2025-12-10 21:09:33 +00:00
spaced4ndyandEvgeny Poberezkin 92a9579e69 agent: option to add SQLite aggregates to DB connection (#1673)
* agent: add build_relations_vector function to sqlite

* update aggregate

* use static aggregate

* remove relations

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-12-04 08:58:14 +00:00
Evgeny cf9b7e5b6a agent: option to add SQLite functions to DB connection (#1674)
* agent: option to add SQLite functions to DB connection

* add module
2025-12-03 16:10:35 +00:00
spaced4ndyandEvgeny Poberezkin 3016b929b4 agent: make createConnection and setConnShortLink apis support setting all link data fields (#1665)
* agent: make createConnection and setConnShortLink apis support setting all link data fields

* add functions

* refactor

* refactor

* fix tests

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-11-05 07:08:33 +00:00
Evgeny Poberezkin 1ae3e8d0be 6.5.0.3 2025-10-23 08:34:11 +01:00
Evgeny 1329fc726f smp: support client notices (#1659)
* agent: support client notices

* improve

* fix, test

* rename

* cleanup

* send and process notices in more cases

* dont delete

* dont remove notice on other permanent errors

* dont remove notice if there is no notice ID in queue

* add server to error

* allow deleting

* only use notice if key hash matches
2025-10-17 18:34:59 +01:00
spaced4ndyandEvgeny 234aeb81dd agent: add subscription status to connection stats (#1658)
* agent: add subscription status to connection stats

* wip

* conn status

* format

* refactor

* refactor

* m

* shorter

* shorter

---------

Co-authored-by: Evgeny <evgeny@poberezkin.com>
2025-10-13 17:53:28 +01:00
spaced4ndyandEvgeny Poberezkin 1dbc15b2e6 agent: sync connections (#1654)
* agent: sync subscriptions

* remove comment

* add shouldDelete flag

* compare api

* remove instance

* query

* rename

* refactor

* functor

* JSON instances

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-10-10 05:41:42 +00:00
Evgeny 80aa56cbcc agent: track which queues need subscribing for iOS NSE (#1657)
* agent: track which queues need subscribing for iOS NSE

* fix down migration

* fix, cleanup
2025-10-09 18:33:27 +01:00
Evgeny 9cda20381f agent: subscribe all connections (#1655)
* agent: subscribe all connections

* query, version

* BoolInt

* add query to errors

* Revert "add query to errors"

This reverts commit 32a1f7fe11.

* fix optional field

* version

* limit number of in-flight subscriptions to 35000
2025-10-09 13:43:48 +01:00
Evgeny 318ddf692a smp server: import/export to/from PostgreSQL with one command, deprecation notice for journal storage (#1653) 2025-10-09 13:31:57 +01:00
Evgeny 11a4859f8e agent: batch processing of subscription results and errors (#1652)
* agent: batch processing of subscription results and errors

* run agent tests with in-memory server storage

* version

* non empty errors

* size
2025-10-07 14:49:38 +01:00
Evgeny a3d1a72eb0 agent: optimize subscriptions memory usage (#1651)
* agent: optimize subscriptions memory usage more (do not store subscribed queues in memory) WIP

* use new session subscriptions data

* version

* remove old data structure

* remove version

* batch deletions

* test TSessionSubs

* comment
2025-10-06 15:21:17 +01:00
Evgeny Poberezkin 17b71cf673 Merge branch 'stable' 2025-10-05 22:03:58 +01:00
Evgeny 779222d1a7 agent: optimize subscriptions memory usage (#1649)
* agent: optimize subscriptions memory usage

* version

* remove typeclass

* disable subscription to test memory usage

* disable delivery and commands

* disable reading connections

* enable subscriptions

* RcvQueueCred

* RcvQueueCred postgres

* fix

* optimize keys

* version

* rename

* comments
2025-10-05 13:35:54 +01:00
Evgeny Poberezkin 538dcb6a4c 6.4.6.0 2025-10-04 14:49:42 +01:00
Evgeny Poberezkin 98115b6db1 6.5.0.2 2025-09-30 12:37:28 +01:00
Evgeny 9854caca33 agent: optimize subscriptions (#1645)
* agent: optimize subscriptions

* simplify

* clean up
2025-09-30 12:34:42 +01:00
spaced4ndy c8b551dcf7 agent: fix file reception error in postgres (#1647) 2025-09-30 08:32:45 +01:00
Evgeny 99f40ae109 support for additional database migrations (#1644) 2025-09-27 11:21:38 +01:00
sh 3a9381b966 ci/scripts: execute git version if chmod fails (#1643) 2025-09-23 09:08:56 +01:00
Evgeny Poberezkin 54a2a6c905 6.5.0.1 2025-09-17 09:04:03 +01:00
Evgeny 7b7616ce7e agent: batch loading of connections with PostgreSQL client (#1639)
* agent: batch loading of connections with PostgreSQL client

* batch more

* optimize getPendingCommandServers

* fix Bool conversion

* enable all tests

* cleanup
2025-09-16 14:28:06 +01:00
sh b020a08ea0 ci: disable aarch64 tests (#1638) 2025-09-15 09:02:34 +01:00
Evgeny Poberezkin 2737e7d602 6.5.0.0.1 2025-09-15 07:52:17 +01:00
Evgeny d3754b36bc smp server: do not use queue cache with PostgreSQL message storage (#1637)
* smp server: do not use queue cache with PostgreSQL message storage

* fix loading queues via notifier IDs
2025-09-15 07:51:27 +01:00
Evgeny Poberezkin 112cd9d5f4 Merge branch 'stable' 2025-09-14 14:42:05 +01:00
Evgeny 8a7991a376 agent: option to set SQLite database backup path (#1636)
* agent: option to set SQLite database backup path

* fix test compilation
2025-09-14 12:41:49 +01:00
Evgeny 6f4d0f5377 Merge pull request #1630 from simplex-chat/db-messages
Feature branch: PostgreSQL message store
2025-09-13 19:24:59 +01:00
Evgeny 9cfdae3494 smp server: uniterruptible mask for DB operations (#1635) 2025-09-13 19:08:07 +01:00
Evgeny Poberezkin 62183699db 6.5.0.0 2025-09-13 11:37:59 +01:00
Evgeny 9be9a76b42 smp server: improve message expiration (#1634)
* smp server: limit by time the queues to export journal messages for

* pass queue/msg thresholds separately

* reset db connection on errors

* Revert "smp server: limit by time the queues to export journal messages for"

This reverts commit d3bc0cba4b.

* fix test compilation

* flag to expire messages

* improve test

* expire messages newer than quota
2025-09-13 11:35:34 +01:00
Evgeny Poberezkin 80f7be6dd7 Merge branch 'master' into db-messages 2025-09-13 09:31:23 +01:00
Evgeny 83721240a4 reset db connection on errors (#1633) 2025-09-13 09:30:01 +01:00
Evgeny 6c66cf367a smp server: set message counts correctly after import (#1632) 2025-09-12 13:08:11 +01:00
Evgeny a137d01c90 smp server: improve counts management and expiration for postgres message store (#1631)
* smp server: improve counts management and expiration for postgres message store

* one update

* fix expiration
2025-09-12 11:39:35 +01:00
Evgeny bac6ea6e91 smp server: store messages in PostgreSQL (#1622)
* smp server: store messages in PostgreSQL

* stored procedures to write and to expire messages

* function to export messages

* move all message functions to PostgreSQL, remove delete trigger

* comments

* import messages to db

* fix message import, add export

* fix export

* fix export

* fix compilation flags

* import messages line by line

* fix server start with database storage

* fix compilation

* comments
2025-09-11 20:22:55 +01:00
Evgeny 0c1030cf02 smp server: faster export (#1626)
* smp server: faster export

* flush

* export messages with PostgreSQL database

* remove flush

* batch message writes

* Revert "batch message writes"

This reverts commit 61fb3c3b9e.

* remove $!

* fast journal export

* another approach

* Revert "smp server: remove dependency of message size on the version (#1627)"

This reverts commit 8fea15245a.

* style

* faster?

* cleanup

* cleanup

* refactor

* refactor

* concurrent read messages

* Revert "concurrent read messages"

This reverts commit 05a32e6491.

* concurrent read/write

* parameter to export to/import from another message log file

* Revert "parameter to export to/import from another message log file"

This reverts commit 4e88b035c4.

* Revert "concurrent read/write"

This reverts commit a8eab1f7e3.
2025-09-10 18:22:58 +01:00
Evgeny Poberezkin 23aff6b0b1 Revert "smp server: remove dependency of message size on the version (#1627)"
This reverts commit 8fea15245a.
2025-09-09 09:56:48 +01:00
sh 3335cd5500 ci: add aarch64 builds (#1624) 2025-09-09 08:55:37 +01:00
spaced4ndyandEvgeny Poberezkin a4f049d8da agent: parameterize withWork, getWorkItem with StoreError; parameterized Binary for SQLite (#1617)
* agent: parameterize withWork StoreError

* getWorkItem

* export

* binary

* remove handleWrkErr AnyStoreError constraint

* put AnyError in AnyStoreError constraint

* move typeclass

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-09-08 16:38:08 +01:00
Evgeny 8fea15245a smp server: remove dependency of message size on the version (#1627) 2025-09-08 11:28:42 +01:00
sh 42a2c1fc57 ci: fix git in docker (#1623) 2025-09-08 09:41:20 +01:00
Evgeny Poberezkin 7e98b3103f 6.4.5.2 2025-09-07 15:13:19 +01:00
Evgeny Poberezkin 2a61085f07 backward compatible encoding for missing NetworkError 2025-09-07 15:04:06 +01:00
Evgeny 4386dd8b5a smp server: option to expire messages when new message is sent (now off by default) (#1625)
* smp server: option to expire messages when new message is sent (now off by default)

* fix test
2025-09-07 09:02:32 +01:00
Evgeny Poberezkin 50ddd630be 6.4.5.1 2025-09-03 11:12:20 +01:00
Evgeny cb3250e7b4 servers: better socket leak prevention during TLS handshake, add NetworkError type to better diagnose connection errors (#1619)
* servers: better socket leak prevention during TLS handshake

* log tcp connection errors

* more detailed network error

* log full address

* rename error

* add encodings for NetworkError

* refactor

* comment

* bind

* style

* remove parameters of NETWORK error from encoding
2025-09-02 16:07:37 +01:00
Evgeny Poberezkin 0319addd2b 6.4.5.0 2025-09-01 18:57:09 +01:00
Evgeny 77bf76e1f9 smp server: fix in-memory server not restoring queue/service associations after 2+ restarts (#1618)
* smp server: fix in-memory server not restoring queue/service associations after 2+ restarts

* fix test, do not reuse database
2025-09-01 17:45:31 +01:00
Evgeny beafac1f73 agent: make agent workers usable from other contexts (#1614) 2025-08-29 08:33:55 +01:00
Evgeny Poberezkin a2d777bda0 6.4.4.1 2025-08-23 20:05:10 +01:00
Evgeny e48bedeaf2 servers: fix server pages when source code is not specified, include commit SHA in printed version and in web page. (#1608)
* smp server: fix server pages when source code is not specified

* servers: include git commit in version

* flexible alpn

* fix test

* fix ghc 8.10.7 build
2025-08-23 19:59:00 +01:00
Evgeny Poberezkin a2d35281b2 6.4.4.0 2025-08-18 16:22:24 +01:00
spaced4ndy 46035af9a3 agent: add context to SEMsgNotFound error (#1604) 2025-08-18 10:59:57 +01:00
Evgeny Poberezkin 4b7fc34fe3 enable all tests 2025-08-17 10:40:12 +01:00
Evgeny 96e8b4a146 smp server: wrap all queries in transactions (#1603)
* smp server: wrap all queries in transactions

* fix test

* fix schema test
2025-08-17 10:38:20 +01:00
Evgeny 2cedb66667 agent: chat relay address type for short links (#1602) 2025-08-15 07:24:37 +01:00
Evgeny e345671c76 agent: extend xrcp certificate validity 1 hour in the past, to allow out of sync clocks (#1601) 2025-08-14 22:48:11 +01:00
Evgeny 86fb2cddc5 smp server: support short SimpleX addresses in server information / pages (#1600) 2025-08-12 07:31:32 +01:00
Evgeny Poberezkin 931c533a3d 6.4.3.0 2025-08-05 12:30:28 +01:00
Evgeny 79ba60e3ad agent: update contact request server hosts to match server in short link (#1597) 2025-08-05 12:29:50 +01:00
Evgeny fb477b24d7 smp server: support short link URI as queue identifier in control port commands (#1596) 2025-08-05 08:18:39 +01:00
Evgeny Poberezkin 9f263e8f3e docs: changelog 2025-08-02 09:21:44 +01:00
Evgeny Poberezkin db325cb81f 6.4.2.0 2025-07-31 09:21:11 +01:00
Evgeny b167d01f8a rfc: using the same profile from multiple devices (#1588) 2025-07-30 20:54:03 +01:00
Evgenyandsh f4e7469f96 xftp server: prometheus metrics (#1595)
* xftp server: prometheus metrics

* update

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>

---------

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2025-07-30 16:18:49 +01:00
Evgeny 4647d69d4b smp server: do not include previously blocked queues in stats, prevent leak of client threads (#1593) 2025-07-30 11:12:57 +01:00
sh 9ab071d62c ci: fix reproduce-schedule (#1594) 2025-07-30 09:17:28 +01:00
Evgeny Poberezkin f4c09ac51f Merge branch 'stable' 2025-07-29 09:09:36 +01:00
Evgeny Poberezkin 846be50f72 6.4.1.0 2025-07-21 15:22:47 +01:00
Evgeny c0f357d817 servers: control port session improvements (#1591)
* servers: prohibit changing role during control port session

* quota for blocked queues

* allow disabling blocking and quota

* fix test

* fix INI file
2025-07-21 15:19:43 +01:00
Evgeny 40fc09a93d smp server: additional stat counter for ntf credentials created together with the queue (#1589)
* smp server: additional stat counter for ntf credentials created together with the queue

* fix prometheus

* fix test

* fix qSub
2025-07-18 09:26:25 +01:00
Evgeny 2a90a2c552 smp protocol: create notification credentials via NEW command that creates the queue (#1586)
* smp protocol: create notification credentials via NEW command that creates the queue

* create ntf subscription for queues created with ntf credetials

* do not create ntf credentials when switching connection to another queue
2025-07-16 16:46:45 +01:00
sh fc581bf729 github/workflows: fix reproduce schedule (#1587) 2025-07-16 11:42:14 +01:00
Evgeny Poberezkin b6ea025333 docs: update build badge 2025-07-15 10:03:27 +01:00
Evgeny Poberezkin d3e2d9ac5b docs: changelog v6.4 2025-07-13 15:20:26 +01:00
Evgeny Poberezkin 85a7fbf538 6.4.0.12 2025-07-12 14:19:32 +01:00
Evgeny 62733ef4c1 smp server: refactor subscriptions and delivery in order to always response SOK on subscription with an optional message to follow. (#1573)
* smp server: refactor subscriptions and delivery

* metric for time between MSG and ACK

* cleanup

* refactor pattern match for ghc 8.10.7

* time buckets

* split max time metric

* histogram

* fix
2025-07-12 14:18:38 +01:00
Evgeny 99e59b73a3 agent: fix message delivery in case one of the connections has no snd queue for any reason - it could break delivery to all connections (#1585)
* agent: fix message delivery in case one of the connections has no snd queue for any reason - it could break delivery to all connections

* simplify

* comment

* refactor, also postpone failing on ratchet sync send prohibited errors

* postpone failing on connection errors to allow subsequent connections succeed
2025-07-10 10:21:12 +01:00
Evgeny 384a3352cf ntf server: more efficient status update query (#1584) 2025-07-08 23:01:57 +01:00
Evgeny Poberezkin 7a04fdff2e 6.4.0.11.3 2025-07-07 18:50:31 +01:00
Evgeny ba3c75e58c smp server: correctly track if ntf service is subscribed and total subscribed queues count (fixes race condition between NSUB and NSUBS from notification server) (#1583) 2025-07-07 18:49:47 +01:00
Evgeny Poberezkin 1062ccc5c3 6.4.0.11.2 2025-07-07 10:07:54 +01:00
Evgeny 36f05e272e agent: support different timeouts for interactive and background requests (#1582)
* agent: support different timeouts for interactive and background requests

* fix tests

* use one constructor for the first request and for retries
2025-07-07 09:38:52 +01:00
Evgeny Poberezkin 660c4293f0 6.4.0.11.1 2025-07-05 11:35:27 +01:00
spaced4ndyandEvgeny Poberezkin 1b8613d767 build: do not exclude postgres modules for client_library flag (#1579)
* don't exclude postgres modules for client_library flag

* fix build with client and server postgres flag

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-07-04 17:31:57 +01:00
Evgeny bfa52c4ba5 docs: update RFC for short links to match the implementation (#1581) 2025-07-03 18:09:02 +01:00
spaced4ndy c5eb66038b agent: allow to accept contact requests after address is deleted (#1580) 2025-07-03 09:29:39 +00:00
Evgeny Poberezkin a46edd60f0 6.4.0.11 2025-06-30 21:50:21 +01:00
Evgeny b4bcfd325b agent: better handling errors during connection handshake retries (#1578)
* agent: handle invitation connection handshake errors

* fix/test retries for connecting via address
2025-06-25 19:06:00 +01:00
Evgeny 976bd3a389 agent: short link encodings (#1577) 2025-06-24 10:06:44 +01:00
Evgeny Poberezkin bf27c846da 6.4.0.10.1 2025-06-20 13:49:17 +01:00
Evgeny 455360205c smp server: prometheus histogram for message confirmation times (ACK) (#1575)
* time buckets

* split max time metric

* histogram

* histogram for confirmed delivery times

* gaugehistogram

* fix created, _ in gauge_histogram

* remove comments

* fix metrics
2025-06-20 13:46:59 +01:00
Evgeny 79c67f2026 agent: JSON encoding for connection mode (#1576) 2025-06-19 11:21:29 +01:00
Evgeny c8928626fc ntf server: do not log subscription status updates on subscribing/unsubscribing (#1574) 2025-06-19 09:15:07 +01:00
Evgeny b47d28a22a smp server: prometheus metrics for delievered and not acknowledged messages (to monitor stuck deliveries) (#1572) 2025-06-17 21:10:09 +01:00
Evgeny c5b7d3c7af agent: use flexible (small/large) string encoding for contact link data, for backward/forward compatibility (#1569)
* agent: use large string encoding for contact link data

* size-dependent user link data encoding

* tag large user data encoding with \255
2025-06-16 11:11:59 +01:00
Evgeny Poberezkin d950012530 6.4.0.10 2025-06-14 21:06:54 +01:00
Evgeny Poberezkin 27b1f48929 Merge branch 'stable' 2025-06-14 21:05:59 +01:00
Evgeny Poberezkin 3d62a383d5 6.4.0.3.1 2025-06-14 14:19:31 +01:00
Evgeny 6ac7101f4f agent: make decoding for short link data forward compatible (#1568) 2025-06-14 14:13:14 +01:00
Evgeny 65cc19842c servers: restore constant time for absent queue/wrong key (regression in 6.4-beta.9) (#1567) 2025-06-14 07:10:51 +01:00
Evgeny 656f290660 agent: fix updating short invitation link data (#1566) 2025-06-13 21:27:46 +01:00
Evgeny Poberezkin 643c3c3b3e 6.4.0.9 2025-06-13 09:48:20 +01:00
Evgeny da37384335 smp server: batch commands (#1559)
* protocol: refactor types and encoding

* clean

* smp server: batch commands (#1560)

* smp server: batch commands verification into one DB transaction

* ghc 8.10.7

* flatten transmission tuples

* diff

* only use batch logic if there is more than one transmission

* func

* reset NTF service when adding notifier

* version

* Revert "smp server: use separate database pool for reading queues and creating service records (#1561)"

This reverts commit 3df2425162.

* version

* Revert "version"

This reverts commit d80a6b74c5.
2025-06-12 23:05:04 +01:00
Evgeny 1658048c2c Revert "smp server: use separate database pool for reading queues and creating service records (#1561)" (#1564)
This reverts commit 3df2425162.
2025-06-12 11:04:43 +01:00
Evgeny 27d38518e1 agent: use PQ keys in contact request data inside link container (but not in contact request link); use PQ keys in invitations sent to contact addresses (#1563)
* agent: use PQ keys in contact request data inside link container (but not in contact request link); use PQ keys in invitations sent to contact addresses

* do not use PQ keys in the link with old address versions
2025-06-11 18:22:37 +01:00
Evgeny cf8088ac6a smp server: do not require ALPN for HTTP to fix Android asset links, do not use port 443 in server-server SMP connections (#1562)
* log alpn

* always use HTTP when SNI is sent, regardless of ALPN

* decide credential based on SNI

* do not use web port in SMP/Ntf servers connecting to SMP servers

* simpler

* refactor

* fix
2025-06-10 22:09:07 +01:00
Evgeny Poberezkin 1e82104224 6.4.0.8 2025-06-09 14:20:25 +01:00
Evgeny 46ff37c362 ntf server: additional statistics (#1558)
* ntf server: additional statistics

* version

* fix stats

* add stats to track notifications without active token

* refactor

* fix stats parser

* version
2025-06-09 14:14:27 +01:00
Evgeny 3df2425162 smp server: use separate database pool for reading queues and creating service records (#1561) 2025-06-09 12:47:35 +01:00
Evgeny 5241f5fe5e rfc: client certificates for servers using SMP protocol as clients (opertors' chat relays, notification servers, service bots) (#1534)
* rfc: client certificates for high volume clients (opertors' chat relays, notification servers, service bots)

* client certificates types (WIP)

* parameterize Transport

* protocol/schema/api changes

* agent API

* rename command

* agent subscriptions return local ClientServiceId to chat

* verify transmissions

* fix receiving client certificates, refactor

* ntf server: remove shared queue for all notification subscriptions (#1543)

* ntf server: remove shared queue for all notification subscriptions

* wait for subscriber with timeout

* safer

* refactor

* log

* remove unused

* WIP service subscriptions and associations, refactor

* process service subscriptions

* rename

* simplify switching subscriptions

* SMP service handshake with additional server handshake response

* notification delivery and STM persistence for services

* smp server: database storage, store log, fix encoding for STORE error, replace String with Text in locks and error

* stats

* more stats

* rename SMP commands

* service subscriptions in ntf server agent (tests fail)

* fix

* refactor

* exports

* subscribe ntf server as service for associated queues

* test ntf service connection, fix SOKS response, fix service associations not removed in STM storage

* INI option to support services

* ntf server: downgrade subscriptions when service is no longer supported, track counts of subscribed queues

* smp protocol: include service certificate fingerprint in the string signed over with entity key (TODO two tests fail)

* fix test

* ntf server prometheus stats, use Int64 in SOKS/ENDS responses (to avoid conversions), additional error status for ntf subscription

* update RFC

* refactor useServiceAuth to avoid ad hoc decisions about which commands use service signatures, and to prohibit service signatures on other commands

* remove duplicate service signature syntax check from checkCredentials, it is checked in verifyTransmission

* service errors, todos

* fix checkCredentials in ntf server, service errors

* refactor service auth

* refactor

* service agent: store returned queue count instead of expected

* refactor serverThread

* refactor serviceSig

* rename

* refactor, rename, test repeat NSUB service association

* respond with error to SUBS

* smp server: export/import service records between database and store log

* comment

* comments

* ghc 8.10.7
2025-06-06 08:03:47 +01:00
Evgeny 8e86c97a13 servers: include supported ALPNs in server transport config (#1557) 2025-06-03 13:35:35 +01:00
Evgeny 90e8c3adf6 smp agent: use static RNG function to avoid dynamic C stub created by Haskell FFI wrapper (#1556) 2025-06-02 20:11:27 +01:00
Evgeny 56851365b1 servers: parameterize SMP agent to optimize subscriptions (#1555)
* servers: parameterize SMP agent to optimize subscriptions

* refactor ClientCommand to avoid additional map in the notification server agent

* rename
2025-06-01 16:14:52 +01:00
Evgeny a9814bb6d3 smp server: parameterize server by store type to avoid boxing/unboxing client handles (#1554)
* smp server: parameterize server by store type to avoid boxing/unboxing client handles

* ghc 8.10.7
2025-05-29 22:17:28 +01:00
Evgeny Poberezkin 3ad8bd15a6 6.4.0.7 2025-05-29 13:14:09 +01:00
spaced4ndy 4c33d8ac43 agent: To and FromField instances for AConnShortLink (#1553) 2025-05-29 10:59:36 +01:00
spaced4ndyandEvgeny Poberezkin a94ca62624 agent: allow changing invitation link data before it is secured (#1552)
* agent: setInvitationShortLink api

* Eq instance

* allow changing link data on server, refactor

* fix

* encodings

* remove link data after connection

* Revert "encodings"

This reverts commit f8e254cca9.

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-05-29 10:44:16 +01:00
Evgeny 53b72469b6 refactor types (#1551) 2025-05-27 22:31:02 +01:00
Evgeny Poberezkin f80ed32a06 6.4.0.6 2025-05-25 17:28:39 +01:00
Evgeny 07eaf9157b smp server: allow getting and deleting short links for the old contact queues (#1549)
* smp server: allow getting and deleting short links for the old contact queues

* fix verifaction of legacy contact queues

* test
2025-05-25 17:03:02 +01:00
Evgeny 56ea2fdd56 refactor types for DB entity (#1548) 2025-05-24 18:19:11 +01:00
Evgeny ffecd4a17a parameterize transport by peer type (client/server) (#1545)
* parameterize transport by peer type (client/server)

* LogDebug level when test is retried

* support "flipped" HTTP2, fix test retry to avoid retrying pending tests

* move sync to the end of the tests
2025-05-24 14:34:22 +01:00
Evgeny Poberezkin dae649fb87 6.4.0.5 2025-05-24 14:21:53 +01:00
Evgeny 57a77f75c1 smp server: support adding link data to contact addresses created before July 2024 (#1547) 2025-05-24 14:20:25 +01:00
spaced4ndy 18e73b8aa7 agent: pass CRClientData to setContactShortLink (#1546)
* agent: pass CRClientData to setContactShortLink

* fix

* fix
2025-05-23 18:21:36 +01:00
Evgeny af9ca59e51 smp server: optimize concurrency and memory usage, refactor (#1544)
* smp server: optimize concurrency and memory usage, refactor

* hide clients IntMap

* reduce STM contention

* comment

* version

* correct stats for subscriptions

* version

* comment

* remove subscribed clients from map

* version

* optimze, refactor

* version

* debug test

* enable all tests

* remove test logs

* retry failed tests with debug logging

* increase test timeout

* sync between tests
2025-05-23 12:52:18 +01:00
Evgeny Poberezkin d352d518c2 6.4.0.4 2025-05-21 08:56:01 +01:00
Evgeny f0dc600016 ntf server: remove shared queue for all notification subscriptions (#1543)
* ntf server: remove shared queue for all notification subscriptions

* wait for subscriber with timeout

* safer

* refactor

* log
2025-05-21 08:55:32 +01:00
Evgeny Poberezkin f44ea0a6d8 6.4.0.3 2025-05-15 21:01:49 +01:00
Evgeny f7d31d4c02 Merge pull request #1524 from simplex-chat/ntf-storage
ntf server: PostgreSQL database storage (feature branch)
2025-05-15 21:00:58 +01:00
Evgeny b90e25a3a5 ntf server: fix repeat token registration when it became invalid (regression) (#1539) 2025-05-15 19:56:48 +01:00
Evgeny Poberezkin cf4b9f669d agent: always enable periodic notifications 2025-05-15 15:16:44 +01:00
Evgeny Poberezkin e417d35cce Merge branch 'master' into ntf-storage 2025-05-14 22:20:55 +01:00
Evgeny Poberezkin deaec3cce2 6.4.0.2 2025-05-07 07:24:06 +01:00
Evgeny 7bbd99644a agent: fix query that updates last message timestamps for notification processing (#1536) 2025-05-06 23:19:58 +01:00
Evgeny cb59a449dd agent: return error and message absence differently when getting notification messages (#1535)
* agent: return error and message absence differently when getting notification messages

* fix test

* mapM

* inline nse functions, release lock on error or no message
2025-05-06 16:20:01 +01:00
Evgeny a632eea75b protocol: parsing for UTCTime to stop on "," and ";" (#1532) 2025-05-04 21:11:06 +01:00
Evgeny 3d10c9bf9e agent: handle cases when last message ts is not set for notifications; set last ts for "stale" notifications when messages expired and queue is empty, to prevent repeated processing (#1531)
* agent: handle cases when last message ts is not set for notifications; set last ts for "stale" notifications when messages expired and queue is empty, to prevent repeated processing

* only log errors if they exist

* only set last ts for queue that delivered notification
2025-05-02 11:21:39 +01:00
Evgeny 2f0cdc40af ntf server: fix returned last notifications (#1530) 2025-04-30 14:57:04 +01:00
Evgeny 0a3d014f5d ntf server: priority db pool for notifications and client commands, concurrent resubscriptions, CLI options (#1529)
* ntf server: option to skip specific tokens when importing store log

* logs

* batch later

* subscribe in parallel, bigger queues

* ntf server: test schema and migrations

* optimize subscriptions

* log level

* resubscribe before starting

* better subscribing

* pooled concurrent resubscriptions

* 5

* async resubscribe

* typo

* priority pool

* db batch size
2025-04-30 11:40:32 +01:00
Evgeny 7d0115daec ntf server, agent: send all periodic notifications from one thread, only to old active clients or new clients with periodic notification mode (#1528)
* ntf server, agent: send all periodic notifications from one thread, only to old active clients or new clients with periodic notification mode

* send different type via subscription queues

* option to compact store log on start
2025-04-28 12:18:55 +01:00
Evgeny f024ab1c3f ntf server: prometheus metrics (#1527)
* ntf server: save prometheus stats

* info metrics

* fix test
2025-04-27 13:25:44 +01:00
Evgeny f4bc1f0926 ntf server: skip duplicates when importing tokens and subscriptions (#1526)
* ntf server: skip duplicates when importing tokens and subscriptions

* skip imported last notifications when no token or subscription present

* fix skipped imported notifications count

* all tests

* fix test
2025-04-26 23:37:55 +01:00
Evgeny 42dbb887f7 ntf server: allow retries when creating subscriptions, prohibit subscriptions with the same queue but another notifier key or token (#1525)
* ntf server: allow retries when creating subscriptions, prohibit subscriptions with the same queue but another notifier key or token

* sync files in the test

* refactor
2025-04-25 19:41:18 +01:00
Evgeny 850d2fa423 ntf server: PostgreSQL database storage (#1519)
* ntf server: PostgreSQL database storage

* ntf server: import/export stubs

* ntf server postgres db functions

* some notifications tests pass

* notifications tests pass

* import/export notification store logs

* fix ntf server CLI

* log in parralel

* update subscription statuses using executeMany

* fix import/export

* refactor

* fix queries

* prohibit token_id and subscription_id updates, dont update tokens and subscriptions on conflict, improve server insertion, remove duplicate tokens for import, remove subscriptions without tokens for import

* comment

* remame

* increase test delay
2025-04-25 16:47:39 +01:00
Evgeny 08b84deba4 agent: option to use web port by default for preset servers only (#1523)
* agent: option to use web port by default for preset servers only

* shorten/restore short links in agent, add encodings for SMP web port setting

* decouple preset domains from preset servers for short links

* refactor, rename
2025-04-24 17:11:52 +01:00
Evgeny Poberezkin ec5a60430d 6.4.0.1 2025-04-22 22:01:16 +01:00
Evgeny e4d4b51c59 smp server: fix/test database import (#1521) 2025-04-22 21:59:27 +01:00
Evgeny Poberezkin 6b60f8bab6 Revert "ntf server: optimize in-memory storage (#1516)"
This reverts commit afb338a41a.
2025-04-21 21:01:24 +01:00
shum aa9b93eee5 ci: retry only in stable or release 2025-04-21 19:06:04 +01:00
Evgeny afb338a41a ntf server: optimize in-memory storage (#1516)
* ntf server: optimize in-memory storage

* test

* ntf server: fix store log parser for token status
2025-04-21 17:12:16 +01:00
Evgenyandshum 1e29f7c811 ci: adjust output, retry tests on failure, include hashes in release notes (#1520)
* scripts/reproduce-builds: rename with prefix and adjust output

* ci: rerun tests and include hashes in release

* ci: fix variables and really skip 8.10.7

* ci: fix prepare step

* ci: setup swap for 8.10.7

* ci: fix upload and formatting

* ci: properly format hashes

* ci: simplify

---------

Co-authored-by: shum <shum@liber.li>
2025-04-21 17:05:31 +01:00
Evgeny Poberezkin d11be15295 ntf server: fix store log parser for token status 2025-04-16 13:01:14 +01:00
Evgeny Poberezkin 305f79d2a6 6.4.0.0 2025-04-14 10:52:05 +01:00
Evgeny Poberezkin af988f774e Merge branch 'stable' 2025-04-14 10:51:34 +01:00
Evgeny Poberezkin 82e389298a docs: CHANGELOG 2025-04-14 10:51:23 +01:00
Evgeny c784d5ce0c Merge pull request #1495 from simplex-chat/short-links
smp protocol: short 1-time invitations and contact address links
2025-04-14 09:58:09 +01:00
Evgeny c2c4730953 smp server: serve .well-known folder via server pages (#1514) 2025-04-13 22:02:48 +01:00
Evgeny Poberezkin ccdd8e1775 agent: additional encodings for short links 2025-04-13 13:43:27 +01:00
Evgeny Poberezkin 307a784174 Merge branch 'master' into short-links 2025-04-13 10:51:32 +01:00
Evgeny Poberezkin 9abc0fa88d 6.3.2.0 2025-04-11 23:22:48 +01:00
Evgeny 76aad61f00 smp server: update server page for short links to correctly change URI for apps (#1511)
* smp server: update server page for short links to correctly change URI for apps

* change page titles
2025-04-11 23:18:23 +01:00
Evgeny 37ce109009 smp server: .well-known folder for server pages for SimpleX apps to handle preset server links (#1510)
* smp server: .well-known folder for server pages for SimpleX apps to handle preset server links

* add short link group path

* test
2025-04-11 18:30:43 +01:00
Evgeny a66163dc46 agent: additional short link types and encodings (#1509) 2025-04-11 12:29:44 +01:00
Evgeny Poberezkin bdf8bf093c smp server: make symlinks relative 2025-04-10 20:58:33 +01:00
Evgenyandsh b7a9542213 smp server: short links and owners for channels (#1506)
* smp server: short links and owners for channels

* types

* support mutliple rcv keys

* fix down migration, test/create server schema dump

* reduce schema dump

* parameterize type for link data by connection type

* return full connection link data

* test version

* change short link encoding

* test: print pg_dump output

* server pages, link encoding

* fix connection request when queue data and sender ID are created for old servers

* test, change pattern

* ci: install postgresql tools in runner (#1507)

* ci: install postgresql tools in runner

* ci: docker shell abort on error

* fix pattern for ghc 8.10.7

* patch ConnReqUriData SMP encoding to preserve queue mode after decoding

* test for RKEY

* fix/test store log with RKEY

---------

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2025-04-10 19:09:47 +01:00
Evgeny Poberezkin 3a3f9fd51e Merge branch 'master' into short-links 2025-04-10 11:51:37 +01:00
Evgeny 6dc9d76ed3 smp agent: handle client/agent version downgrades after connection was established (#1508) 2025-04-10 11:50:19 +01:00
Evgeny a2a4b80af4 agent: padded encryption for link data, shorten/restore short links, tests (#1505)
* agent: padded encryption for link data, tests

* lambda

* test short links via proxy

* tests: server persistence with short links

* rfc: group links

* shorten, restore, test short links encoding

* rfc
2025-04-05 07:45:39 +01:00
Evgeny 7ec0ae3bb5 agent: types and encodings to use from the app (#1504)
* agent: types and encodings to use from the app

* use action forks
2025-04-03 16:31:22 +01:00
Evgeny 2c5530c9f0 agent: binary protocol encoding for connection request (#1503)
* agent: binary protocol encoding for connection request

* enable tests

* test
2025-04-01 22:32:49 +01:00
Evgeny 94ee3ceced agent: replace sndSecure with queueMode in queue URIs and records (#1502)
* agent: replace sndSecure with queueMode in queue URIs and records

* fix test
2025-03-31 16:54:29 +01:00
Evgeny Poberezkin dd67de4d71 Merge branch 'master' into short-links 2025-03-31 16:54:07 +01:00
Evgeny 285fd93c32 smp server: notices about server pages and port 443, enable store log by default (#1501)
* smp server: notices about server pages and port 443, enable store log by default

* enable store log by default for ntf and xftp servers, fix tests
2025-03-31 16:41:11 +01:00
Evgeny 56bec06856 smp protocol: remove creating notifications from NEW command, new ideas for notifications (#1500) 2025-03-30 19:56:16 +01:00
Evgeny 04cbed90fb agent: set/update and delete contact short link data (#1499)
* agent: set/update and delete contact short link data

* delete contact link data, tests

* comments

* type signature for GHC 8.10.7
2025-03-30 11:17:25 +01:00
Evgeny c1a6647f19 agent: join connection when 1-time invitation short link is already secured (#1496)
* agent: join connection when 1-time invitation short link is already secured

* do not pass short link to join

* delete short link record after connection
2025-03-29 09:42:56 +00:00
Evgeny Poberezkin 1dd677eec2 Merge branch 'master' into short-links 2025-03-28 19:50:18 +00:00
Evgeny 7636bc7491 smp server: remove locks for deleted queues, additional statistics for objects in memory (#1498)
* smp server: remove locks for deleted queues, additional statistics for objects in memory

* version

* reduce queue cache usage

* less caching, refactor

* comments

* revert version
2025-03-28 18:51:54 +00:00
sh 79adb83782 ci: add reproduce builds workflow (#1497)
* ci: add reproduce builds workflow

* ci: trigger webhook and build every day

* ci: change secrets
2025-03-27 16:06:32 +00:00
Evgeny b83d897650 smp protocol: short links and other changes from RFC (#1489)
* smp protocol: short links types and other changes from RFC

* add fields for queue link ID and data

* create queue and ntf credentials with NEW command

* all tests

* simplfiy types, update rfc

* update rfc

* include SenderId in NEW request in case queue data is sent

* store queue data and generate link ID if needed

* update rfc

* agent API and types

* SMP commands and persistence for short links

* SMP client functions for short links

* agent client functions for short links

* create rcv queue with short link (TODO secret_box)

* encryption and encoding for link data, postgres client migration

* test creating short link

* get link and data, tests

* comments

* type signature
2025-03-26 17:26:27 +00:00
Evgeny Poberezkin 0c3b25706a Merge branch 'stable' 2025-03-23 18:18:07 +00:00
sh c3f57beafd scripts: fix script for reproducible builds (#1492)
* scripts: fix script for reproducible builds

* scripts: add automatic hash comparasion + compute file with checksums

* scripts: silence cleanup

* ci/scripts: enable parallel build (#1493)
2025-03-23 18:17:27 +00:00
Evgeny Poberezkin aace3fd2fb 6.3.1.0 2025-03-22 13:51:55 +00:00
sh 2e67ed9c4c ci/scripts: refactor reproducible builds (#1490)
* ci: build cache, remove 20.04, minor refactor

* scripts: update build reproducibility script

* script: add postgresql reproducibility

* script: no cache when building image

* scripts: force symlinks

* scripts: also download prebuilt postgresql binary

* scripts: add missing app var
2025-03-22 13:27:11 +00:00
Evgeny 614fa2b163 smp server: reduce queue expiration/idle intervals, skip expiring very old queues (#1488)
* smp server: reduce idle queue interval and queue expiration interval

* only expire recent queues (not very old)

* fix

* version
2025-03-20 08:57:47 +00:00
Evgeny 903e96bdfa rfc: smp queue protocol changes (#1485)
* rfc: smp queue protocol changes

* questions

* update

* update

* correction
2025-03-19 10:47:57 +00:00
Evgeny 5c0adcbbff Merge pull request #1467 from simplex-chat/postgres
SMP server PostgreSQL storage - feature branch (merge, dont squash)
2025-03-18 16:48:25 +00:00
sh b2f16eeff4 ci: seperate builds (pg/standard) (#1486) 2025-03-18 09:42:53 +00:00
Evgeny 6db79808aa smp server: use COPY to import store log to postgres db, improve concurrency and error handling (#1487)
* smp server: use COPY to import store log to postgres db

* compact queues when importing to postgres

* mempty

* version

* handle errors while expiring, mask async exceptions while getting queue

* whitespace

* version
2025-03-18 09:40:22 +00:00
Evgeny f4b55bfc0c smp server: CLI option to compact store log with PostgreSQL database (#1484)
* smp server: CLI option to compact store log with PostgreSQL database

* version

* fix test
2025-03-16 18:47:02 +00:00
Evgeny fe64d42db1 smp server: split postgres support to a separate executable, to not require postgres library in the main binary (#1482)
* smp server: split postgres support to a separate executable, to not require postgres library in the main binary

* comments

* enable server_postgres flag by default, add CPP option to test

* refactor

* change default for server_postgres to False

* diff
2025-03-16 11:37:25 +00:00
Evgeny 1b5a9f3b0c smp server: do not cache all queues from database while processing expirations (#1483)
* smp server: expire only active queues

* version

* do not cache all queues while processing expirations

* refactor

* foldWithOptions_

* version

* use shared lock when expiring all queues

* use TMVar

* comment

* rename

* remove fold options

* do not create locks in the Map for temporarily loaded queues

* fix

* revert version
2025-03-15 20:11:30 +00:00
Evgeny fdf8bd7ee2 smp server: mask database store operations from async exception for state consistency (#1481)
* smp server: mask database store operations from async exception for state consistency

* remove unused, comments
2025-03-13 13:57:46 +00:00
Evgeny 019a32a623 smp server: optionally append store log with postgres storage (without loading and compacting, for debugging during migration) (#1480)
* smp server: optionally maintain store log with postgres storage (without loading and compacting, for debugging during migration)

* refactor

* remove comment
2025-03-13 10:25:56 +00:00
Evgeny d44f09d111 tests: pass AStoreType to agent test as a parameter (#1479)
* tests: run agent tests with PostgreSQL SMP servers

* agent tests with postgres database

* enable tests

* fix store log tests

* fix test
2025-03-12 21:47:44 +00:00
Evgeny 6b5de2c51b smp server: exception on invalid migration, check queue storage settings on server start (#1478)
* smp server: check queue storage settings on server start

* fix incorrect postgres migration not throwing exception
2025-03-10 17:28:26 +00:00
shum 9410fb6f16 docker: add libpq dep 2025-03-10 10:03:44 +00:00
Evgeny 7b42aaa132 smp server: expire messages in postgres database, mark queues as deleted, combine tables (#1471)
* smp server: expire messages in postgres database

* tty

* fail if nothing updated in db

* remove old deleted queues

* index

* fix tests
2025-03-10 09:31:50 +00:00
Evgeny Poberezkin e4b9aa9746 Merge branch 'master' into postgres 2025-03-09 21:58:39 +00:00
Evgeny Poberezkin aa26a55df4 Merge branch 'master' into stable 2025-03-09 21:53:03 +00:00
sh 6e505f5c0b scripts: add script to reproduce binaries locally (#1477) 2025-03-08 20:51:20 +00:00
Evgeny Poberezkin a491a1d878 6.3.0.8 2025-03-07 14:30:00 +00:00
shandEvgeny Poberezkin 36f5539b9a ci: introduce reproducible builds (#1476)
* ci: introduce reproducible builds

* ci: return 20.04

* smp server: increase timing test threshold

* ci: test outside docker

* ci: fix test step

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-03-07 14:15:18 +00:00
Evgeny 1a2afe8bfd agent: fix JSON encoding for protocol errors to be compatible with iOS (#1475) 2025-03-06 08:02:27 +00:00
Evgeny Poberezkin 9fece9ce3d 6.3.0.7 2025-03-02 22:45:07 +00:00
Evgeny Poberezkin 2e2ede5968 Merge branch 'master' into postgres 2025-03-02 22:35:28 +00:00
Evgeny 205d4ead1c smp server: remove store log backups when server starts (#1472) 2025-02-27 07:39:05 +00:00
Evgenyandspaced4ndy 80a070a8ea smp server: update STM cache only after PostgreSQL update (#1470)
* refactor journal store

* update postgres store methods to update STM cache only after DB update

* todos

* drop

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* fix GHC 8.10.7

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2025-02-26 11:43:08 +00:00
Evgeny 172540984c agent, smp server: PostgreSQL connection pool (#1468)
* agent, smp server: PostgreSQL connection pool

* simplify, create all connections before start

* rename

* remove imports
2025-02-25 15:33:54 +00:00
Evgenyandspaced4ndy 4dc40bd795 smp server: PostgreSQL queue store (#1448)
* smp server: queue store typeclass

* parameterize JournalMsgStore

* typeclass for queue store

* postgres WIP

* compiles, passes tests

* remove StoreType

* split migrations

* progress

* addQueueRec

* reduce type spaghetti

* remove addQueue from typeclass definition

* getQueue

* test postgres storage in SMP server

* fix schema

* comment

* import queues to postgresql

* import queues to postgresql

* log

* fix test

* counts

* ci: test smp server with postgres backend (#1463)

* ci: test smp server with postgres backend

* postgres service

* attempt

* attempt

* empty

* empty

* PGHOST attempt

* PGHOST + softlink attempt

* only softlink attempt

* working attempt (PGHOST)

* remove env var

* empty

* do not start server without DB schema, do not import when schema exists

* export database

* enable all tests, disable two tests

* option for migration confirmation

* comments

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2025-02-24 15:47:27 +00:00
Evgeny Poberezkin f9d7b1eebc 6.3.0.6 2025-02-23 19:42:16 +00:00
Evgeny ffbc733d58 smp server: remove duplicate progress log (#1466) 2025-02-23 19:40:49 +00:00
Evgeny 2286726d72 smp server: start options maintenance and skip-warnings (#1465)
* smp server: start options `maintenance` and `skip-warnings`

* ignore invalid parsing of the last lines

* parsingErr

* fix
2025-02-22 19:26:03 +00:00
spaced4ndy 1b8110a332 xftp server: restore file status from log (#1461)
* xftp server: restore file blocking info from log

* fix parse

* rework

* update

* rename
2025-02-21 22:43:56 +00:00
sh dad7e1b60c simplex-servers-update: download scripts from tag (#1457)
* simplex-servers-update: download scripts from tag

* set remote version when re-executing the update script

* safeguard variables

* additional checks
2025-02-20 09:26:03 +00:00
Evgeny 72c2ddcf57 agent: move migrations (#1459) 2025-02-18 23:39:29 +00:00
Evgeny a75e138965 smp server: remove empty queues journals when expiring messages of idle queues (#1458)
* smp server: remove empty queues journals when expiring messages of idle queues

* remove unnecessary update

* ci: update action

* rename
2025-02-18 20:04:58 +00:00
Evgeny fa319d798a smp server: remove empty journals when opening message queue (#1456)
* smp server: remove empty journals when opening message queue

* update, do not backup state

* test

* version

* do not close queue state when queue is opened for writing

* comment

* quota = 4

* refactor openMsgQueue to prevent extra state backups

* use interval in config

* version, expire backups after 5 min

* refactor

* test
2025-02-17 23:11:34 +00:00
Evgeny Poberezkin c192339af9 6.3.0.5 2025-02-15 15:36:44 +00:00
Evgeny fa67d128d1 agent: fix deleting messages after delivery to avoid deleting shared message bodies (#1455)
* agent: fix deleting messages after delivery to avoid deleting shared message bodies

* fix, comments

* rename

* comment
2025-02-15 12:40:10 +00:00
Evgeny 55ff581655 SMP client: dont block on writing to sending queues (#1454)
* SMP client: dont block on writing to sending queues

* only fork if full
2025-02-15 11:31:05 +00:00
sh 560b257af7 scripts: simplex-servers-update menu to update only server binaries etc. (#1445)
* scripts/simplex-servers-update: refactor

* simplex-servers-update: don't check empty variable

* simplex-servers-update: minor decoration change

* simplex-servers-update: add new line when reexecuting script

* simplex-servers-update: refactor menus

* simplex-servers-update: fix re-executing

* simplex-servers-update: fix menus

* simplex-servers-update: do not rerended menu every time

* simplex-servers-update: update menus logic

* simplex-servers-update: ask permission to update only if versions differ

* simplex-servers-update: skip empty variable

* simplex-servers-update: include while loop

* simplex-servers-update: update re-executing skip logic

* simplex-servers-update: ignore empty

* simplex-servers-update: s/while/if

* simplex-servers-update: ignore empty variable

* simplex-servers-update: update skipping scripts

* simplex-servers-update: minor fixes

* simplex-servers-update: add spaces

* simplex-servers-update: safeguard files and add cleanup logic

* simplex-servers-update: update menus

* simplex-servers-update: fix binary check

* simplex-servers-update: adjust erasing lines

* simplex-servers-update: simplify bin update logic

* simplex-servers-update: fix binary path

* simplex-servers-update: format errors

* scripts/main/simplex-servers-update: initial refactor2

* simplex-servers-update: incr update

* simplex-servers-update: initial dynamic menu

* simplex-servers-update: misc fixes

* simplex-servers-update: fix VER

* simplex-servers-update: add VER check

* simplex-servers-update: abort early if there's no options available

* simplex-servers-update: add new helper function and update_misc

* simplex-servers-update: pass selection

* simplex-servers-update: non-interactive usage

* simplex-servers-update: add help menu

* simplex-servers-update: misc fixes and new binary update logic

* simplex-servers-update: local path check

* simplex-servers-update: fix change check

* simplex-servers-update: OLF -> OLD

* simplex-servers-update: always return code from functions

* simplex-servers-update: new menu flow

* simplex-servers-update: fixes

* simplex-servers-update: download function + fixes

* simplex-servers-update: s/bins/binaries

* simplex-servers-update: download message

* simplex-servers-update: cleanup
2025-02-15 10:31:19 +00:00
spaced4ndyandEvgeny Poberezkin 7ac80bffcb agent: store shared message body only once (when it is the same across messages when batching) (#1453)
* agent: store shared message body only once (when it is the same across messages when batching)

* rename

* refactor

* refactor

* save bodies and messages in single transaction

* comment

* comment

* comment

* box

* mapME

* box

* ValueOrRef

* remove instances

* refactor

* comments

* test

* refactor

* mapAccumLM compatibility with ghc 8.10.7

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-02-14 18:01:40 +00:00
spaced4ndyandEvgeny Poberezkin 0d8a1a2879 agent: encrypt messages on delivery (#1446)
* agent: save message body once (plan, schema)

* split

* new type

* bs

* encrypt on delivery

* schema

* fix test

* check pad size

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-02-14 12:35:18 +00:00
Evgeny Poberezkin bd97cb0449 6.3.0.4 2025-02-10 15:12:54 +00:00
Evgeny a58d3540ad agent: refactor migrations (#1452)
* agent: refactor migrations

* fix postgres
2025-02-07 17:17:53 +00:00
Evgenyandspaced4ndy 5dbe6337ea ntf server: additional statistics, new invalid token reasons (#1451)
* agent: check ntf token status on registration

* remove check

* update on check

* refactor

* version

* fix

* ntf server: additional statistics

* swap

* version

* more stats

* test, verify invalid

* rename

* exclude test token from stats

* increase delay

* handle invalid token in retry, more reasons

* focus tests

* disable new tests in CI

* fix

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2025-02-07 12:19:11 +00:00
spaced4ndyandEvgeny Poberezkin b633f89c1a agent: check ntf token status on registration (#1450)
* agent: check ntf token status on registration

* remove check

* update on check

* refactor

* version

* fix

* test, verify invalid

* rename

* increase delay

* disable new tests in CI

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2025-02-07 11:36:29 +00:00
Evgeny 944a22a2fb ntf server: record token invalidation reason, add date of the last token activity (#1449)
* ntf server: record token invalidation reason, add date of the last token activity

* update time

* rename

* optional

* include token ID in delivery error

* version

* protocol version

* fix, log error
2025-02-06 17:11:35 +00:00
Evgeny ce24f83b64 refactor STM queues (#1447) 2025-02-05 12:04:27 +00:00
Evgeny Poberezkin 45373e7f1f 6.3.0.3 2025-01-31 13:01:03 +00:00
sh efe71cd598 docker: refactor (#1438)
* docker: refactor

* github/docker: bump actions and adjust smp ports
2025-01-30 09:22:13 +00:00
Evgeny ae41717b9b smp server: use origin client version when processing proxied command, fixes old client sending to new server (#1443)
* smp server: pass origin client version when processing proxied command, fixes old client sending to new server

* version

* version
2025-01-28 22:04:46 +00:00
Evgeny e78ab60c97 build: fix postgres (#1444) 2025-01-28 22:02:41 +00:00
Evgeny b3c8358a43 agent: combine connection deletion events (#1442) 2025-01-25 08:52:40 +00:00
sh be81fe1f74 scripts/systemd: update services (#1440) 2025-01-24 13:37:56 +00:00
sh 817f5e1737 scripts/systemd: update services (#1440) 2025-01-24 13:35:33 +00:00
Evgeny eda9e36c82 agent: track queries (#1439) 2025-01-24 10:31:50 +00:00
Evgeny Poberezkin 2318975375 6.3.0.2 2025-01-20 16:38:14 +00:00
Evgeny 298666380f servers: handle accept error ECONNABORTED (#1437) 2025-01-20 16:32:42 +00:00
Evgeny 6a9075141f xftp server: use recipient ID in control port to delete and block files, smp server: fix version negotiation (#1434)
* xftp server: use recipient ID in control port to delete and block files

* cap smp proxy agent version at 10

* version

* fix prometheus

* fix

* remove old version support

* log connection parameter on error

* tests

* log sent command tag

* log error and client version

* cap proxy version for previous destination server

* comment, test

* remove logging tag

* remove logs

* version

* SMP version 14

* version

* remove comments

* version
2025-01-20 13:45:49 +00:00
spaced4ndy 488c7082f3 agent: store interface (#1436) 2025-01-20 17:02:39 +04:00
spaced4ndy fdde9863cd agent: add reopenStore function for postgres; run notification tests with postgres (#1435) 2025-01-17 16:27:37 +04:00
Evgeny Poberezkin 9404a3ab63 xftp server: block stats 2025-01-14 10:46:27 +00:00
Evgeny Poberezkin dadf6ec5b6 6.3.0.1 2025-01-13 08:39:41 +00:00
Evgeny c528ea4f31 smp server: update versions (#1433) 2025-01-12 22:11:18 +00:00
Evgeny 3d4e0b06c0 servers: blocking records for content moderation (#1430)
* servers: blocking records for content moderation

* update

* encode BLOCKED as AUTH in old versions

* update

* unblock queue command

* test, status command
2025-01-12 19:34:00 +00:00
spaced4ndy 9d9ec8cd0b agent: remove unused functions (#1432)
* agent: remove unused functions

* cleanup imports
2025-01-10 07:59:17 +00:00
Evgeny 992b42e922 agent: option to enable/disable vacuum after SQLite migration (#1429) 2024-12-28 21:06:42 +00:00
Evgeny 3cf9dacbc0 Merge pull request #1428 from simplex-chat/ep/postgres-compat
agent: restore methods for backwards compatibility with simplex-chat
2024-12-28 11:39:18 +00:00
Evgeny Poberezkin cfde5932fd agent: restore methods for backwards compatibility with simplex-chat 2024-12-28 10:33:22 +00:00
Evgeny 69fb9a9e0e Merge pull request #1417 from simplex-chat/postgres
agent: support postgres backend
2024-12-27 19:52:18 +00:00
Evgeny Poberezkin 0a118f5631 Merge branch 'master' into postgres 2024-12-27 11:58:19 +00:00
spaced4ndy 0b986c1158 postgres: export fromOnlyBI 2024-12-26 20:13:27 +04:00
Evgeny Poberezkin 184a95cd2a 6.3.0.0 2024-12-26 13:54:38 +00:00
spaced4ndy b7a8df40c4 Merge branch 'master' into postgres 2024-12-25 12:15:41 +04:00
spaced4ndyandEvgeny Poberezkin bf28902327 agent: reuse ratchet on repeat join (#1426)
* agent: reuse ratchet on repeat join

* check status

* update

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-25 00:07:45 +00:00
Evgeny Poberezkin 426bf68763 6.2.2.0 2024-12-24 21:04:58 +00:00
spaced4ndy e557dc86e7 Merge branch 'master' into postgres 2024-12-20 17:18:54 +04:00
Evgeny Poberezkin f5cef2556b build: move type to client_library module 2024-12-20 13:12:11 +00:00
spaced4ndyandEvgeny Poberezkin cf66aadc20 postgres: store implementation, conditional compilation (#1421)
* postgres: implementation wip

* to from field

* agent store compiles

* methods

* create store

* tests wip

* migration tests pass

* tests compile

* fix tests

* tests wip

* bool int

* tests wip

* tests wip

* more boolint

* more fixes

* more fields pass

* more fixes

* binary

* instances, binary

* test passes

* remove todos, more tests pass

* fix conflict

* fix bool

* fix sequence breaking

* fix insertedRowId

* skip ratchet re-synchronization tests

* after test

* file tests

* after test

* rename

* remove comment

* format

* remove unused

* suppress notices

* fixes

* move

* fix

* instance

* instance2

* fix

* instances

* comment

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-12-20 15:54:58 +04:00
sh 77a5ed2ec6 6.2.1.0 (#1424) 2024-12-19 20:45:17 +00:00
Evgenyandsh 4d640c16aa smp server: log prometheus metrics (#1411)
* smp server: log prometheus metrics

* save metrics

* diff

* lines

* version

* do not include Prometheus into client

* corrections

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>

* corrections

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>

* corrections

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>

* add timestamp to metrics

* remove type

* remove version

---------

Co-authored-by: sh <37271604+shumvgolove@users.noreply.github.com>
2024-12-18 11:19:13 +00:00
Evgeny 0a827307d6 build: remove some modules from the client build (#1418)
* build: remove some modules from the client build

* remove websockets from client_library
2024-12-14 15:22:06 +00:00
spaced4ndy 64149599de postgres: db interfaces wip (sqlite passes) (#1419) 2024-12-12 17:42:58 +04:00
spaced4ndy 07be469f49 postgres: schema (#1416) 2024-12-10 18:53:01 +04:00
Evgeny Poberezkin 79e9447b73 6.2.0.7 2024-12-07 14:36:50 +00:00
spaced4ndy 9893935e7c agent: fix acceptContact inconsistent state after app restart (#1412) 2024-12-05 19:27:21 +04:00
Evgeny Poberezkin 966b9990e0 6.2.0.6 2024-12-04 19:57:11 +00:00
spaced4ndy bef1e38295 xftp: add debug info to "too many recipients" error (#1410) 2024-12-04 19:56:33 +00:00
Evgeny Poberezkin 4b43cb8054 6.2.0.5 2024-12-03 12:33:32 +00:00
Evgeny 38ad3c046e agent: function to check if contact address is the same (#1409) 2024-12-02 11:41:06 +00:00
Evgeny b2afe6f40c rfc: moving queue records to journal storage and queue blobs, expiring messages in journal storage (#1407)
* rfc: moving queue records to journal storage and queue blobs

* rfc: expiring messages in journal storage

* add comment
2024-11-28 12:22:52 +00:00
Evgeny Poberezkin 601620bdde 6.2.0.4 2024-11-22 19:50:04 +00:00
Evgeny 97104988a3 server: support server roles and operators (#1343)
* server: support server roles and operators

* make server operator optional

* allRoles

* fix test

* different server host in tests

* remove ServerCfg fields used only in UI

* comments

* choose different server for invitation when connecting via address

* fix test in ghc8107

* simplify
2024-11-22 16:43:37 +00:00
Evgeny 45333bd340 smp server: do not open/read journal message queues that are known to be empty (#1406)
* smp server: do not open/read journal message queues that are known to be empty

* cleanup

* version

* close empty queues on first subscription

* revert version
2024-11-22 12:29:31 +00:00
Evgeny bbcb1abfda agent: treat blocked STM and other critical errors that offer restart as temporary (#1405) 2024-11-21 22:01:19 +00:00
Evgeny Poberezkin a64c1aa2c4 6.2.0.3 2024-11-17 09:45:17 +00:00
Evgeny 21fbbf9106 smp server: update message counts during message expiration, increase idle interval (#1404)
* smp server: update message counts during message expiration, increase idle interval

* version

* fix

* flip results

* version
2024-11-17 09:44:40 +00:00
Evgeny 17a0be10fa smp server: expire messages in idle message queues (including not opened) (#1403)
* smp server: expire messages in idle message queues (including not opened)

* use message expiration interval

* simpler

* version

* remove version
2024-11-15 10:26:24 +00:00
Evgeny Poberezkin 3017d14392 6.2.0.2 2024-11-07 08:13:45 +00:00
Evgeny d3275cef48 smp server: combine messages and queue storage to optimise performance, prevent race condition when deleting queue and to avoid "orphan" messages. (#1395)
* smp server: combine queue and message store into one class (WIP)

* keep deleted queue tombstones to prevent race conditions and errors when restoring

* move store log from server to store implementations

* STMQueueStore type class

* fix store closed when messages expired, handle store writing errors

* types

* version

* fix recovery from missing write journal, tests

* version
2024-11-07 08:09:11 +00:00
Evgeny Poberezkin 0fd4aa1e23 6.2.0.1 2024-11-03 21:10:45 +00:00
Evgeny 7c21945569 smp server: option to skip expiring messages on start, read queue state file end only (#1400) 2024-11-03 21:08:07 +00:00
ffecf200d4 build: client_library flag to build only used modules, remove package yaml (#1394)
* move dependencies to library

* remove package.yaml

* remove auto-generated comment

* cleanup target definitions

* add executable options

* add test rtsopts

* revert common options

* don't hide unused packages warning

* Add client_only cabal flag (#1396)

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-11-02 15:34:33 +00:00
Evgeny 58d3c160ef smp server: fix logging progress when importing journal (#1393) 2024-10-28 14:22:18 +00:00
Evgeny Poberezkin 326cf08efa 6.2.0.0 2024-10-28 10:28:54 +00:00
Evgeny Poberezkin d49dc201c3 smp server: log file name on hClose error 2024-10-28 10:26:24 +00:00
Evgeny 6ff4c3f1ce Merge pull request #1375 from simplex-chat/journal-store
smp server: journal message store
2024-10-26 17:03:19 +01:00
Evgeny 5940514f40 smp server: remove queue from map when closing, test (#1392)
* smp server: remove queue from map when closing, test

* remove print

* refactor
2024-10-26 16:14:55 +01:00
Evgeny Poberezkin 916150742e Merge branch 'master' into journal-store 2024-10-26 12:58:32 +01:00
Evgeny Poberezkin 1735b619e8 6.1.3.0 2024-10-26 12:57:43 +01:00
Evgeny 6b61d101dc smp server: fix restoring notifications (#1391) 2024-10-26 12:56:29 +01:00
Evgeny 56ca7bf30e smp server: do not log progress when server starts (#1390)
* smp server: do not log progress when server starts

* test
2024-10-26 10:33:18 +01:00
Evgeny Poberezkin 2a218675b2 Merge branch 'master' into journal-store 2024-10-26 08:12:07 +01:00
Evgeny 8b9eb49b57 smp server: log operation of restore error (#1389)
* smp server: log operation of restore error

* space

* rename

* revert version
2024-10-26 08:11:07 +01:00
Evgeny 3860341584 rfc: iOS notifications 2 (#1334) 2024-10-25 21:42:13 +04:00
Evgeny Poberezkin 9078118a6d Merge branch 'master' into journal-store 2024-10-25 15:43:04 +01:00
Evgeny Poberezkin a7f5297ad1 6.1.2.0 2024-10-25 15:41:30 +01:00
Evgeny 870a60fc5d smp server: reduce memory usage when starting, all servers: prevent losing state on termination while compacting state (#1385)
* smp server: reduce memory usage when starting

* servers: prevent losing state when compacting is incomplete (terminated abnormally)

* fix test

* fix/refactor

* message

* remove .bak

* simpler
2024-10-25 15:39:59 +01:00
spaced4ndyandEvgeny Poberezkin a8471eed5b ntf: get messages for multiple last notifications (#1378)
* ntf: get messages for multiple last notifications (#1352)

* ntf: separate get ntf conns api (#1379)

* ntf: separate get ntf conns api

* nonempty

* update

* update

* remove single get api

* fix test

* refactor

* refactor

* ntf: batch get connections (#1387)

* ntf: batch get apis

* works

* fix

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-10-25 15:39:41 +04:00
Evgeny a3351ad060 smp server: fix export of journal storage (#1384)
* smp: fix export of journal storage

* catch errors when exporting messages
2024-10-25 09:11:47 +01:00
Evgeny 2322f5bf59 smp server: logging format, mask/handle exceptions during journal store operations (#1381)
* smp server: fixed logging format for journal store errors

* version

* colon

* logs

* refactor

* space

* remove comment

* log file name in fixFileSize

* logError

* stricter

* process all queues more efficiently

* use monoid for queue processing

* expire messages concurrently

* concurrently 2

* Revert "concurrently 2"

This reverts commit c1aee1f22c.

* Revert "expire messages concurrently"

This reverts commit fc53137cdb.

* show queue directory or ID in errors

* foldM

* mask_

* try

* mask more

* refactor

* command to delete journal

* uninterruptibleMask_ when writing to state file

* fix ghc8.10.7

* version

* revert version
2024-10-24 19:44:47 +01:00
Evgeny c9c075fd49 smp server: merge quota messages and set queue to "over quota" state after restoring, server tests with journal and memory store (#1380)
* smp: run server tests with journal and memory store

* merge quota messages, set queue to "over quota" state after restoring

* fix test
2024-10-23 09:17:23 +01:00
Evgeny 1484523164 smp server, journal store: check file size on append, validate queue state (#1377)
* smp server: journal store queue state validation

* validate state on update, refactor

* handle message parsing errors

* typo

* fix test, throw exception when file is smaller than expected

* fix

* fix test in ghc 8.10.7
2024-10-22 12:09:51 +01:00
Evgeny dc17402896 smp server: updated journal store API (#1374)
* core: updated journal store API

* parameterize JournalState by read/write type

* collect stats when importing/expiring

* compare stored stats and store for messages and notifications

* simplify
2024-10-21 16:50:07 +01:00
Evgeny 0414ea59f0 smp server: journal message store (#1370)
* smp server: remove STM function from MsgStore

* polymorphic MsgStore

* jourmal storage for messages (WIP)

* more journal, test setup

* writeMsg

* test

* tryDelMsg

* delMsgQueue

* remove MsgStoreClass instance of existential wrapper for Msg stores

* store config

* extract common logic out of store instances

* add store type to config

* open journals, cache last message, tests pass

* CLI commands

* refactor import/export messages

* cli commands to import/export journal message store

* export journal without draining, import/export tests

* journal command

* import/export progress

* better progress info

* only log queue state once when importing

* logs

* handle IO errors in journal store, return as STORE error

* recover from state file errors

* fix message files after crash

* fix messages folder
2024-10-21 11:50:30 +01:00
Evgeny Poberezkin 967afaf802 6.1.1.0 2024-10-18 12:47:27 +01:00
Evgeny 77ee9bea38 smp protocol: add STORE error (#1372) 2024-10-18 10:13:01 +01:00
Evgeny e0cd28a65a smp server: stop server faster (#1371)
* smp server: stop server faster

* .702

* asks

* fix leak

* kill client when server stopped

* remove version

* fix version
2024-10-18 09:12:59 +01:00
Evgeny Poberezkin 7b6c86c6c1 docs: correction 2024-10-14 08:32:19 +01:00
Evgeny Poberezkin 1d0a418011 rfc: rename file 2024-10-12 17:12:45 +01:00
Evgeny Poberezkin 9253f5647a Merge branch 'stable' 2024-10-12 16:30:54 +01:00
Evgeny Poberezkin c41bfe831d 6.1.0.7: changelog for v6.1.0 2024-10-11 13:01:12 +01:00
Alexander BondarenkoandEvgeny Poberezkin c9af7e327e smp server: graceful shutdown on SIGINT (#1360)
* smp-server: graceful shutdown on SIGINT

* .501

* clean up

* remove version

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-10-11 12:49:15 +01:00
Evgeny 8870442830 smp server: reject SKEY with different key earlier (at verification step), add tests (#1366) 2024-10-11 12:13:52 +01:00
spaced4ndyandEvgeny Poberezkin 9ad26c97a1 agent: prepareConnectionToAccept api (#1365)
* agent: prepare conn (plan)

* implement

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-10-11 11:05:14 +01:00
spaced4ndy e1201f0967 agent: test db indexes (#1364) 2024-10-09 15:16:08 +04:00
Evgeny Poberezkin ca2202e3c8 6.1.0.6 2024-10-07 22:41:53 +01:00
Evgeny f24ee70138 clients: do not send SNI (#1363) 2024-10-07 22:40:30 +01:00
Evgeny Poberezkin 8981cae3c2 ci: always generate change log 2024-10-07 22:22:02 +01:00
Evgeny Poberezkin b8971a31bc 6.1.0.5 2024-10-07 10:24:19 +01:00
Evgeny e01398a31a agent: send MSGNTF on NO_MSG error (#1362)
* agent: send MSGNTF on NO_MSG error

* simplify
2024-10-07 10:22:58 +01:00
Evgeny f871f20172 smp server: fix notification delivery (#1350)
* .401

* stats for undelivered notifications

* logs, stats

* control port show ntf client IDs

* check that Ntf client is still current and that queue is not full, drop notifications otherwise

* prevent losing notifications when client is not current or queue full

* add log when no notifications, remove some logs

* reduce STM transaction

* revert version change
2024-10-07 09:01:28 +01:00
Evgeny 80d3518d55 tests: store log (#1348)
* 6.1.0.4

* tests: store log

* random

* more tests
2024-10-06 21:21:08 +01:00
spaced4ndyandEvgeny Poberezkin 49935473ae ntf: agent to notfiy MSGNTF on successful ack (#1355)
* ntf: agent to notfiy MSGNTF on successful ack

* move msgntf

* release lock earlier

* fix tests

* do not release get lock

* fix

* diff

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-10-06 19:48:22 +01:00
Evgeny 0705d05078 smp: transport block encryption (#1317) 2024-10-06 18:05:06 +01:00
Evgeny 97d45fb2a6 smp server: pass server information via CLI during server initialization (#1356)
* smp server: pass server information via CLI during server initialization

* more info

* enable client expiration by default, disable port 8000

* update
2024-10-06 12:34:44 +01:00
Evgeny 50e4abf836 ntf server: control port (#1354)
* ntf server: control port

* version .405

* control

* use own_server_domains from INI file

* fix subs by server in control port

* bigger queues

* ntf server: only print subscriptions per own server when they are > 0

* fix tests

* revert version change

* dont import listThreads in ghc 8.10.7
2024-10-06 12:33:14 +01:00
spaced4ndy b130ea439a server: fix msgGetNoMsg stat (#1358) 2024-10-04 17:19:05 +04:00
sh 0e921d3258 scripts: preparations for new deployments (#1357)
* scripts/install: allow specifying version

* scripts/update: allow specifying version

* scripts/services: allow 443 for smp, allow infinite startup
2024-10-04 09:57:07 +01:00
Evgeny d0f65329fb ntf server: enable pings on ntf subscriptions (#1353) 2024-10-03 12:12:55 +01:00
Evgeny Poberezkin 61b2b9df1a Re-commit: smp server: persist notifications to avoid losing them when ntf server is offline (#1336)
This reverts commit 0ba3e69872.
2024-10-02 12:23:46 +01:00
Evgeny Poberezkin 04e4a37d85 6.1.0.4 2024-10-02 09:51:29 +01:00
Evgeny Poberezkin 0ba3e69872 Revert "smp server: persist notifications to avoid losing them when ntf server is offline (#1336)"
This reverts commit 0739f7b702.
2024-10-02 09:49:43 +01:00
Evgeny Poberezkin 7488923fc3 6.0.6.0 2024-10-02 09:24:29 +01:00
Evgeny cf559ee43f smp server: fix notifiers lost when compacting store log (#1347) 2024-10-02 09:24:04 +01:00
Evgeny 2ab0c2a7c6 smp server: fix notifiers lost when compacting store log (#1347) 2024-10-01 22:28:01 +01:00
spaced4ndy 243548631a ntf: repeat failed token deletions (#1346) 2024-10-01 16:30:14 +04:00
Evgeny Poberezkin da79d544cf 6.1.0.3 2024-09-30 18:27:14 +01:00
spaced4ndy e808825c95 ntf: fix endless worker loop (#1345) 2024-09-30 15:29:14 +01:00
Evgeny Poberezkin a9576935cf 6.1.0.2 2024-09-29 18:54:04 +01:00
spaced4ndyandEvgeny Poberezkin 9cf6c97137 ntf: batch ntf worker command processing (#1337)
* ntf: batch ntf worker command processing

* remove comment

* change batch size

* wip

* catch

* refactor

* refactor

* batch check

* refactor

* reschedule

* increase ntfSubCheckInterval

* first check interval

* check more statuses

* refactor, remove foldr'

* refactor 2

* refactor client

* refactor 3

* ntf server: improve support for batched commands (#1340)

* re-create ntf subscriptions on NTF AUTH errors

* name

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-09-29 18:50:38 +01:00
Evgeny 0739f7b702 smp server: persist notifications to avoid losing them when ntf server is offline (#1336)
* ntf server: types for storing notifications

* fix tests

* remove comments

* batch NMSGs test

* fix test

* thread to expire notifications

* persist notifications on restart

* optimize

* refactor

* remove reverse

* control port stats
2024-09-29 18:50:16 +01:00
Evgeny e12710fa55 smp server: show version on server page (#1341) 2024-09-29 18:46:38 +01:00
EvgenyandIC Rainbow 2a120dfe57 smp server: Allow serving HTTPS and transport on the same port (v2) (#1327)
* smp-server: Allow serving HTTPS and transport on the same port

* update rfc

* servers: refactor TLS credentials

* provide server credentials in SNI hook

* determine TLS server params dynamically, when starting the server

* remove alpn from TransportServerConfig to decide it dynamically where server is started

* diff

* combine HTTP and SMP on the shared port

* Update to SockAddr

* Fix params and web.https parser

* Switch fork urls

* WIP: add smpServerTestStatic test

* Update warp-tls repo

* shared connection tests

* cleanup

* Add protocol tests

* rename cert file, enable both ports and web by default

* terminate with message on missing credentials

* test cert file

* client option to use port 443 as default SMP port

* use SNI in non-SMP clients

* supported

* remove TODO

* advice

* fix test build

* Add RSA-4096 check for web creds, fix test

* Remove directory listing from static app

* message

* messages

* update log tests

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
2024-09-28 23:15:17 +01:00
Evgeny 3c18c4b66a servers: refactor TLS credentials (#1326)
* servers: refactor TLS credentials

* provide server credentials in SNI hook

* determine TLS server params dynamically, when starting the server

* remove alpn from TransportServerConfig to decide it dynamically where server is started
2024-09-28 22:21:08 +01:00
Evgeny 21eee2b548 smp servers: separately account for subscribed clients, to disconnect those without subscriptions more effectively (and to be able to deliver notifications to subscribed ntf servers) (#1339) 2024-09-28 19:59:53 +01:00
Evgeny Poberezkin 03168b9fbf 6.1.0.1 2024-09-26 12:23:31 +01:00
spaced4ndy 41e1b2bd39 ntf: use common function for differentiating temporary errors (#1333) 2024-09-25 18:40:59 +01:00
Evgeny 1afa0206a5 smp: increase server and default agent queue sizes (#1335) 2024-09-25 18:33:45 +01:00
spaced4ndyandEvgeny Poberezkin 81fcdf8ac9 ntf: batch smp worker command processing (#1331)
* ntf: refactor smp worker (separate command processing into steps)

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-09-25 15:00:06 +04:00
Evgeny 1f67b403de agent: function to get multiple work items (#1330) 2024-09-25 10:55:45 +01:00
Evgeny 8e7f3f7b27 agent: return full argument with batched results (#1332) 2024-09-24 16:16:52 +01:00
Evgeny Poberezkin e6e9f1d5a5 Merge branch 'stable' 2024-09-24 12:36:05 +01:00
Evgeny Poberezkin 4268b90763 6.0.5.0 2024-09-24 12:34:46 +01:00
Evgeny 856137b763 rfс: efficient message storage (#1295)
* rfs: efficient message storage

* typos

* update
2024-09-24 12:29:44 +01:00
Evgeny d0ee505c3a rfc: protocol for SMP queue data blobs and short links (#1309)
* rfc: protocol for SMP queue data blobs and short links

* update

* private rendezvous
2024-09-24 12:29:11 +01:00
Evgeny 7dcac19a67 xrcp: session encryption with forward secrecy (#1328)
* xrcp: session encryption with forward secrecy

* update doc

* accept keys as parameters

* docs: counter

* spec corrections
2024-09-24 09:22:26 +01:00
Evgeny 22260cd719 agent: transport isolation mode "Session" (default) to use new SOCKS credentials when client restarts or SOCKS proxy configuration changes (#1321)
* agent: transport isolation mode "Session" (default) to use new SOCKS credentials when client restarts or SOCKS proxy configuration changes

* fix test
2024-09-22 22:22:05 +01:00
spaced4ndyandEvgeny Poberezkin bef11e4cbe agent: ntf errs (#1316)
* agent: ntf errs

* refactor

* errs

* refactor

* simplify

* inline

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-09-20 20:08:42 +04:00
Evgeny Poberezkin d5a65656f5 test: socks proxy parameter encoding 2024-09-16 13:15:30 +01:00
Evgeny Poberezkin 309ef3766c 6.1.0.0 2024-09-16 07:28:50 +01:00
Evgeny Poberezkin bde7b97f6a Merge branch 'stable' 2024-09-15 21:38:22 +01:00
Evgeny 398dbeedea servers: allow multiple server ports for SMP and Ntf servers (#1319)
* servers: allow multiple server ports for SMP and Ntf servers

* fix
2024-09-15 21:37:09 +01:00
Evgeny fa772af6c6 agent: support socks proxy without isolate-by-auth, with and without credentials (#1320)
* agent: support socks proxy without isolate-by-auth, with and without credentials

* add unit tests

* make xftp use correct SOCKS credentials

* rename

* support ipv6 in brackets, test parsing

* constant

* textToHostMode

* space
2024-09-15 21:36:31 +01:00
Evgeny Poberezkin 9ebdd2b0ea Merge branch 'stable' 2024-09-14 19:00:36 +01:00
Evgeny bec4e5e038 smp: reduce max message sizes (#1318) 2024-09-14 17:34:29 +01:00
spaced4ndy ea67b34ba7 ntf: batch supervisor commands (#1313) 2024-09-13 15:26:57 +04:00
spaced4ndyandEvgeny Poberezkin e247f691cb agent: do not reuse notifier credentials when re-creating notification subscription; on deleting ntf sub delete directly on smp server skipping ntf server (#1311)
* agent: change notifier credentials when re-creating notification subscription

* skip ndel

* enable tests

* fix race

* Revert "fix race"

This reverts commit ed9b18e8a7.

* delete record

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-09-12 14:02:51 +04:00
Evgeny Poberezkin 896b2425a4 xrcp: use SHA3-256 in hybrid key agreement (#1302)
This reverts commit 62133ceb24.
2024-09-11 19:32:40 +01:00
spaced4ndy 30e15b881f agent: don't prohibit deletion of notifications token if different is passed (fixes turning notifications off after importing database) (#1308)
* agent: don't prohibit deletion of notifications token if different is passed (fixes turning notifications off after importing database)

* ignore error

* notify
2024-09-11 19:30:59 +01:00
Evgeny Poberezkin 24ded9e5a0 Merge branch 'master' into stable 2024-09-11 19:03:29 +01:00
Evgeny Poberezkin f5e666ae4f 6.0.4.0 2024-09-11 18:51:26 +01:00
Evgeny Poberezkin 62133ceb24 Revert "xrcp: use SHA3-256 in hybrid key agreement (#1302)"
This reverts commit 67d38090ed.
2024-09-11 18:45:44 +01:00
Evgeny 3b50e1fb7d ntf server: only use SOCKS proxy for servers without public address (#1314) 2024-09-11 18:41:40 +01:00
Evgeny 7c25b3b1e0 smp protocol: send DELD when subscribed queue is deleted (#1312)
* smp protocol: send DELD when subscribed queue is deleted

* fix, test

* refactor

* send DELD event only if the client supports it (version 10); send END otherwise

* fix test

* notify on notifier rotation

* increase test delays
2024-09-11 13:16:51 +01:00
Evgeny a70bd02c67 xftp server: round down file creation time to 1 hour (#1310) 2024-09-10 08:14:05 +01:00
Evgeny 990dcec348 smp server: add created/updated/used date to queues to manage expiration (#1306)
* smp server: add created/updated/used date to queues to manage expiration, all: make Map updates strict in value

* remove strict

* remove time precision

* diff

* style

* only update when time changed
2024-09-09 14:53:11 +01:00
Evgeny 75712641ee rfc: stabilize iOS notifications (#1221) 2024-09-09 17:07:32 +04:00
spaced4ndy 946e16339e agent: process last notification from list (#1307) 2024-09-09 16:42:14 +04:00
spaced4ndy 092ed088ca ntf: support for multiple messages encoding (#1305) 2024-09-09 16:03:17 +04:00
sh 7000431249 scripts/servers: update stopscript (#1286)
* scripts/servers: update stopscript

* Major refactoring

* archive -> backups

* simplify logic

* sort files by timestamp
2024-09-09 10:19:17 +01:00
Evgeny dab1980d79 xftp: report receive file error with redirected file ID, when redirect is present (#1304)
* xftp: report receive file error with redirected file ID, when redirect is present

* fix test
2024-09-09 08:08:16 +01:00
Evgeny 344a295845 agent: error when user record is not in database (#1303) 2024-09-08 16:50:22 +01:00
Evgeny 67d38090ed xrcp: use SHA3-256 in hybrid key agreement (#1302) 2024-09-08 15:45:45 +01:00
Evgeny a9e8d02593 server: bind control port server only to 127.0.0.1 for better security (in case of firewall misconfuguration) (#1280) 2024-09-05 13:48:09 +01:00
Evgeny d859f27999 ntf server: remove debug logging (#1284) 2024-09-05 13:26:34 +01:00
Evgeny e86338d555 smp server: fewer map updates on re-subscriptions (#1297)
* smp server: fewer map updates on re-subscriptions

* temp version

* replace Client with ClientId in queues

* version

* version

* comments

* reduce threads when sending ENDs

* revert version
2024-09-05 13:25:41 +01:00
Evgeny 137ebc1cad servers: reduce memory used for period stats (#1298) 2024-09-02 23:12:08 +01:00
Evgeny d84a49b85a smp server: split and reduce STM transactions (#1294) 2024-09-02 17:06:31 +01:00
Evgeny d5efe3406a agent: fix race when sending a message to the deleted connection (#1296) 2024-09-02 15:07:16 +01:00
Evgeny Poberezkin d559a66145 6.0.3.0 2024-08-30 12:55:17 +01:00
Evgeny ce6777b68d newtype for server entity IDs, fix TRcvQueues (#1290)
* put DRG state to IORef, split STM transaction of sending notification (#1288)

* put DRG state to IORef, split STM transaction of sending notification

* remove comment

* remove comment

* add comment

* revert version

* newtype for server entity IDs, fix TRcvQueues

* Revert "put DRG state to IORef, split STM transaction of sending notification (#1288)"

This reverts commit 517933d189.

* logServer
2024-08-30 12:50:02 +01:00
Evgeny 655e7ad7d5 smp server: get message queue faster, avoiding STM contention if queue exists, split transaction for notification delivery (#1289)
* put DRG state to IORef, split STM transaction of sending notification (#1288)

* put DRG state to IORef, split STM transaction of sending notification

* remove comment

* remove comment

* add comment

* revert version

* smp server: get message queue faster, avoiding STM contention if queue exists

* IORef for counter

* Revert "put DRG state to IORef, split STM transaction of sending notification (#1288)"

This reverts commit 517933d189.

* version

* remove IORef

* split notification delivery transations

* revert version
2024-08-30 11:53:22 +01:00
Evgeny 9596a03139 servers: reduce STM transactions (#1287)
* servers: reduce STM transactions

* switch stats and pending ENDs to IORef

* more IORef, split pending ENDs to use in one thread
2024-08-29 13:18:12 +01:00
Evgeny 2e7e476f81 smp server: remove "expensive" stats (#1285) 2024-08-28 18:20:44 +01:00
Evgeny 2c9ad74599 smp server: batch END responses when subscribed client switches (version 2) (#1283)
* fix incorrect entity ID for notification subscription END when queue is deleted

* throttle sending ENDs

* fix stats

* clean up
2024-08-28 10:14:41 +01:00
Evgeny Poberezkin aa60bd6770 use strict Maps, fix stats for sent END batches 2024-08-26 19:35:57 +01:00
Evgeny 16cf5c8628 smp server: stats for END events and for SUB/DEL event batches (#1281)
* smp server: count queued and sent END events

* fix

* shadowing

* stats for batches

* fix
2024-08-26 14:58:18 +01:00
Evgeny Poberezkin 56986f82c8 6.0.2.0 2024-08-24 14:54:32 +01:00
Evgeny 53e829a21c agent: store query errors, reduce slow query threshold to 1ms (#1277)
* agent: collect query errors stats

* simplify

* test

* use microseconds

* parens

* revert change to track all queries, reduce threshold to 1 ms
2024-08-24 14:51:26 +01:00
Evgeny ab4acadcc1 agent: fix stuck connection commands when server is not responding (#1276)
* agent: fix stuck connection commands when server is not responding

* fix

* rename

* test SKEY retry
2024-08-24 13:54:20 +01:00
Evgeny 325e679bc4 ntf server: reduce PING interval to 1 minute, only mark subscriptions with END if it is received by the currently active client (#1275) 2024-08-24 11:34:59 +01:00
Evgeny Poberezkin d81cae712e Merge branch 'master' into stable 2024-08-23 14:41:15 +01:00
Evgeny Poberezkin 1d22608f86 6.0.1.0 2024-08-20 18:46:42 +01:00
Evgeny 39f3cf3082 agent: only create notification subscriptions to connections without errors or with temporary errors (#1270) 2024-08-20 16:05:50 +01:00
sh 5be81c562f scripts: update install/update (#1271) 2024-08-20 15:57:29 +01:00
Evgeny 2bdda1ff1f servers: safe compacting of store logs (#1269)
* servers: safe compacting of store logs

* fix
2024-08-20 13:36:37 +01:00
Evgeny ac930dff30 smp server: possible race when creating client that might lead to memory leak (#1260) 2024-08-20 12:25:58 +01:00
DiogoandEvgeny Poberezkin 1cbf8c0015 agent: support changing user of the new connection (#1267)
* agent: support new connection user id update

* another way for assertion

* add more tests to setConnUserId

* remove fdescribes

* allow rcv connection to change user id

* add functional test to api

* remove fdescribe

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-08-20 10:57:09 +01:00
Evgeny 571d148bdf agent: do not start delivery workers when there are no messages to deliver (#1263)
* agent: use weak ThreadId and forkIO in workers instead of async (reduce memory)

* agent: do not start and exit delivery workers when there are no messages to deliver (#1264)

* agent: exit delivery workers when no messages to deliver

* only start delivery workers when there are pending messages

* fix

* focus test

* enable all tests

* lift

* do not exit workers when there is no work
2024-08-18 21:30:06 +01:00
Evgeny 32a150eb40 agent: enable notifications for all connections (#1262) 2024-08-18 14:07:09 +01:00
EvgenyandAlexander Bondarenko 5ad6e5f2f3 deps: upgrade tls to 1.9 (#1265)
* deps: use tls-2.0

* roll back RCP "cleanup"

* use tls 1.9

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-08-18 13:55:12 +01:00
Evgeny f229e135e3 agent: reduce subscription transactions (#1259)
* agent: reduce subscription transactions

* nub

* remove commented
2024-08-16 10:06:22 +01:00
Evgeny Poberezkin d15d87c111 Merge branch 'master' into stable 2024-08-11 18:06:11 +01:00
Evgeny Poberezkin 0dd52dc69f v6.0.0 (6.0.0.8) 2024-08-09 23:09:09 +01:00
Evgeny 47dd0c4c64 agent: do not send worker crash errors when agent is not active (stopped) (#1256) 2024-08-09 21:10:27 +01:00
Evgeny 7762b4d364 reduce the number of STM transactions (#1255)
* reduce the number of STM transactions

* reduce STM more

* refactor

* remove more
2024-08-09 16:38:46 +01:00
Evgeny Poberezkin 7d8457263b 6.0.0.7 2024-08-08 13:15:01 +01:00
Evgeny fc6b9c0e1b agent: stop statistics and cleanup when chat is stopped (#1251) 2024-08-07 13:14:09 +01:00
Evgeny Poberezkin a76e15fd77 6.0.0.6 2024-08-06 12:39:11 +01:00
Evgeny a017047c52 smp server: fix race when client is marked as subscribed after it is disconnected, preventing its GC (#1250)
* smp server: fix race when client is marked as subscribed after it is disconnected, preventing its GC

* refactor
2024-08-06 08:17:38 +01:00
Evgeny f4d1a33ed8 agent: retry loop that resumes subscriptions as soon as agent is moved to foreground, suspend retry loops while agent is suspended (#1249)
* agent: retry loop that resumes subscriptions as soon as agent is moved to foreground, suspend retry loops while agent is suspended

* reset retry enterval when moving to foreground

* account for network state too

* simplify

* typo

* simplify
2024-08-05 15:09:46 +01:00
Evgeny Poberezkin 03ea151be5 agent: support additional messages for connection in message batches (#1247)
* agent: support additional messages for connection in message batches

* fix, test

* test 2 connections

* use TVar instead of TMVar for GHC 8.10.7
2024-07-30 17:12:29 +01:00
Evgeny Poberezkin 83f8622b23 6.0.0.5 2024-07-29 21:02:22 +01:00
Evgeny Poberezkin 3753379ae4 agent: support dababase access with high priority for more responsive user actions (#1248)
* agent: support dababase access with high priority for more responsive user actions

* rename, export

* fix
2024-07-29 18:57:16 +01:00
Evgeny Poberezkin 2de16cfae8 6.0.0.4 2024-07-28 15:03:32 +01:00
Evgeny Poberezkinandspaced4ndy 5fa3c149e9 smp server: fix server-info, additional stats, allow expiring inactive clients which have prohibit subscriptions only (iOS NSE clients) (#1237)
* smp server: fix server-info

* fix

* faster saving messages

* remove comment

* move ProhibitSub out of TVar

* subscription stats

* stabilize test

* order

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* more notification stats

* count ntf stats

* update server-info

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-07-25 16:06:40 +01:00
Evgeny Poberezkin 9093c7b120 agent, ntf server: only mark subscriptions as pending that were created by the disconnected client (#1242)
* ntf server: only mark subscriptions as pending if the disconnected client is current

* add sessionId to subscribed queue

* add sessionId to subscriptions in ntf server agent

* fix
2024-07-25 13:07:28 +01:00
Evgeny Poberezkin 47ae921b86 use strict Map (#1241) 2024-07-23 08:27:54 +01:00
spaced4ndyandEvgeny Poberezkin c605156302 agent: getAgentSubsTotal api (#1238)
* agent: getAgentSubsTotal api

* move

* export

* count sessions

* simplify

* simplify 2

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-22 15:43:38 +01:00
Evgeny Poberezkin 051bf38bc7 agent: remove delays in notification processing, add notification stats (#1235)
* agent: remove delays in notification processing, add notification stats

* do not replace token after failed check

* more stats

* refactor

* fix

* backwards compatible JSON parsing

* retry deleting subscription on temporary error

* remove attempts to get multiple notification messages

* fix JSON decoding to be backwards compatible
2024-07-22 15:42:34 +01:00
Evgeny Poberezkin 8423c636a8 rename queueId to entId (can be message queue, proxied session and data blob ID to be added) (#1240) 2024-07-22 12:01:38 +01:00
Evgeny Poberezkin cc5732f41f smp server: stats for commands retrieving messages when notifications are received (#1236)
* more stats

* fix stats
2024-07-20 14:10:10 +01:00
spaced4ndy 7565ddd91c agent: initialize ratchet on processing confirmation to support decryption of messages received before allowConnection; return SndQueueSecured from joinConnection, acceptContact (#1233) 2024-07-18 19:54:14 +04:00
Evgeny Poberezkin 0de596dbcf 6.0.0.3 2024-07-18 11:05:53 +01:00
Evgeny Poberezkin e59a098e66 smp server: remove subscriptions from the clients when queues are deleted (#1234)
* smp server: remove subscriptions from the clients when queues are deleted

* remove ntf subscriptions, update stats

* add GET stats
2024-07-18 10:59:48 +01:00
Evgeny Poberezkin 8d56b0ba85 agent: allow starting only XFTP sending workers (#1232)
* agent: allow starting only XFTP sending workers

* use Bool param

* flip bool

* update
2024-07-17 13:01:17 +01:00
Evgeny Poberezkin b40d55c358 6.0.0.2 2024-07-15 20:30:39 +01:00
Evgeny Poberezkin 784d36d691 socks mode encoding (#1231)
* socks mode encoding

* test delays

* test delay

* unused import
2024-07-15 20:28:24 +01:00
Evgeny Poberezkin 1bdfc8ae00 6.0.0.1 2024-07-14 23:21:14 +01:00
Evgeny Poberezkin 291039159f ntf server: use SOCKS proxy to connect to onion-only SMP servers (#1229)
* ntf server: use SOCKS proxy to connect to onion-only SMP servers

* fix test
2024-07-14 23:19:02 +01:00
Evgeny Poberezkin d4fa0af350 ntf: additional tests for token registration when server and device are restarted (#1230)
* ntf: additional tests for token registration when server and device are restarted

* test response timeouts
2024-07-14 17:57:34 +01:00
Evgeny Poberezkin 492d2f86bc smp server: additional control port commands to monitor server state (#1228)
* smp server: additional control port commands to monitor server state

* fix

* space
2024-07-13 22:34:10 +01:00
Evgeny Poberezkin 8dd54ced0e agent: retry proxied command on NO_SESSION error, to prevent failure of proxied interactive commands (like joining connection) (#1227) 2024-07-13 10:06:48 +01:00
Evgeny Poberezkin e56bd0b47b agent: add known servers (#1225)
* agent: add known servers

* test delays

* ServerCfg

* json encoding

* enabledServerCfg

* checkUserServers
2024-07-12 12:41:55 +01:00
spaced4ndy ff2b00a029 agent: change ProxyClientError json encoding (#1226) 2024-07-11 19:27:04 +04:00
Evgeny Poberezkin 017469b2de 6.0.0.0 2024-07-09 13:56:02 +01:00
Evgeny Poberezkin 26979ff6b5 smp server: simplify client subscriptions (#1223) 2024-07-09 08:36:03 +01:00
Evgeny Poberezkin 21abc5cabe smp server: reduce the number of threads by delivering message to subscription when it is sent (#1222)
* smp server: reduce the number of threads by delivering message to subscription when it is sent

* test delay

* test delay
2024-07-08 23:12:01 +01:00
Evgeny Poberezkin 6e76221e07 agent: fix possible dead lock between sending and receiving messages, stress test for message delivery (#1224)
* agent: fix possible dead lock between sending and receiving messages, stress test for message delivery

* deliver events after the lock is released

* delayed delivery in command processing too

* tests: increase message expiration time
2024-07-08 21:47:42 +01:00
Evgeny Poberezkin 743676421d ntf server: simplify and optimize subscriptions in server agent (#1219)
* increase queue size

* simplify

* refactor to optimize memory usage and performance

* comment

* refactor

* test delays
2024-07-07 21:17:12 +01:00
Evgeny Poberezkin 9d0774a58e agent: add queue information (#1217)
* agent: add queue information to "debug delivery" response

* fix test

* rename

* encodings
2024-07-03 19:32:27 +01:00
Evgeny Poberezkin ce732c0efb agent: enable fast handshake (revert #1215) (#1216)
* Revert "agent: disable fast handshake (#1215)"

This reverts commit aa1d8d6c8b.

* remove import

* test delays
2024-07-03 18:05:27 +01:00
Evgeny Poberezkin 1ac0068d22 Merge commit 'f392ce0a9355cd3883400906ae6c361b77ca46ea' into stable 2024-07-03 14:09:54 +01:00
spaced4ndyandEvgeny Poberezkin ae8e1c5e9a agent: servers stats improvements, fixes (#1208)
* agent: reset stats startedAt time in memory

* getAgentSubsSummary

* change sub counting

* ack statistics

* add import

* instance

* Revert "instance"

This reverts commit 1f63740d56.

* Revert "add import"

This reverts commit ef72df8014.

* modify sub counting

* modify conn creation counting

* use int64

* file size stats

* remove import

* ack err counting

* conn del stats

* format

* new data

* add data

* toKB

* restore connCompleted

* use Int for counts

* use rq from scope

* remove getAgentSubsSummary

* fix connCompleted

* fix

* revert disabling stats

* use srv from scope

* combine ack stats

* modify

* comment

* count subs

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-03 12:36:15 +01:00
Evgeny Poberezkin f392ce0a93 5.8.2.0 2024-07-02 13:55:28 +01:00
Evgeny Poberezkin aa1d8d6c8b agent: disable fast handshake (#1215) 2024-07-02 13:40:37 +01:00
Evgeny Poberezkin ae4325d0e7 Merge pull request #1209 from simplex-chat/fast-connection
fast handshake protocol
2024-07-02 08:13:37 +01:00
Evgeny Poberezkin da5f669133 remove diagram 2024-07-01 23:34:31 +01:00
Evgeny Poberezkin 5c839b40c9 Merge branch 'master' into fast-connection 2024-07-01 21:14:32 +01:00
spaced4ndy a50e2e74a5 agent: disable saving stats (#1214)
* agent: disable stat saving

* disable migration

* schema

* disable restore
2024-07-01 21:06:38 +01:00
Evgeny Poberezkin 26cfad5e88 do not use sndSecure when rotating queue (#1213) 2024-07-01 13:42:46 +01:00
Evgeny Poberezkin a99ce6122c secure queue by sender via proxy (proxy SKEY command) (#1210)
* client: secure queue by sender via proxy (proxy SKEY command)

* agent and server: proxy SKEY command
2024-06-30 16:20:54 +01:00
Evgeny Poberezkin a6b542b301 Merge branch 'master' into fast-connection 2024-06-30 15:00:28 +01:00
Evgeny Poberezkin 6a54a58a0d agent: remove legacy statistics (#1211)
* agent: remove legacy statistics

* delays after disposeAgent

* delays

* enable all tests

* more delays
2024-06-30 12:50:42 +01:00
Evgeny Poberezkin 9ee684b0f4 rfc: faster handshake protocol (#1203)
* rfc: faster handshake protocol

* update

* 1 message

* SKEY

* use SKEY for both parties

* test

* update doc

* NEW command parameter

* add k=s param to queue URI

* fix

* add sndSecure field to queues

* make sender key non-optional in SndQueue (WIP, tests fail)

* fast handshake sometimes works (many tests fail)

* correctly handle SKEY retries, avoiding to re-generate the keys

* handle SKEY retries during async connection

* fix most tests (1 test fails)

* remove do

* fix contact requests encoding/tests

* export

* fix: ignore duplicate confirmations, fixes testBatchedPendingMessages

* do not store sndSecure in store log if it is false to allow server downgrade

* add connection invitation encoding tests
2024-06-30 08:36:24 +01:00
spaced4ndyandEvgeny Poberezkin c788692687 agent: servers summary types, api (#1202)
* agent: servers summary types, api [wip]

* encoding

* export

* Revert "export"

This reverts commit cd9f315fe8.

* comment

* rename

* simplify types

* uncomment

* comment

* rework

* comment, exports

* save, restore stats wip

* remove

* rename

* save stats periodically

* sigint, sigterm experiments

* corrections

* remove some proxy stats

* increase stat

* proposed stats

* fields

* Revert "sigint, sigterm experiments"

This reverts commit f876fbd418.

* wip

* retries -> attempts

* errs

* fix

* other errs

* more stat tracking

* sub stats

* remove xftp successes stats

* xftp stats tracking

* revert

* revert

* refactor

* remove imports

* comment

* Revert "refactor"

This reverts commit 26c368d82a.

* Revert "revert"

This reverts commit 4c9e3753b5.

* Revert "revert"

This reverts commit 6f65644053.

* todos

* persistence

* rename, fix

* config

* comment

* add started at to summary

* delete stats on user deletion

* reset api

* move

* getAgentServersSummary collect state logic

* corrections

* corrections

* remove

* rework

* decrease contention

* update

* more stats

* count sentProxied

* count subs

* remove unused

* comment

* remove comment

* comment

* export

* refactor

* cleanup

* intervals

* refactor

* refactor2

* refactor3

* refactor4

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-25 09:42:59 +04:00
Alexander BondarenkoandEvgeny Poberezkin 9e7e0d102d smp-server: conserve resources (#1194)
* transport: force auth params, remove async wrapper

* stricter new messages

* bang more thunks

* style

* don't produce msgQuota unless requested

* strict

* refactor

* remove bangs

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-24 13:15:08 +01:00
d47c099ac9 docs: update protocol specifications (#1204)
* docs: update protocol specifications

* update SMP protocol (WIP)

* add proxy protocol commands and responses, amend envelope sizes in SMP protocol

* docs: update XFTP protocol (#1205)

* docs: update XFTP protocol

* commands

* fix table of contents, move sections

* add about ids

* download encryption

* qualities

* diagram

* crypto

* sending file diagram

* fix svg

* receiving file diagram

* update commands

* update handshake

* Add updated XRCP (#1207)

* add XRCP protocol

* add ToC

* update

* update

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* add XFTP handshake description

* update agent protocol

* fast duplex connection in agent protocol

* update agent protocol

* update overview

* typos

* queue rotation, agent API, updates

* push notifications specification

* add XRCP threat model

* XFTP threat model

* update PQDR

* agent protocol end-to-end encryption

* versions

* remove TOC details

* update

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-06-23 22:53:45 +01:00
Evgeny Poberezkin e371a4a62d Merge branch 'master' into stable 2024-06-16 07:58:05 -07:00
sh 8a3b72458f readme: update compiling from source instructions (#1190) 2024-06-16 07:43:22 -07:00
Evgeny Poberezkin f616cb7b3e 5.8.1.0 2024-06-16 07:36:38 -07:00
spaced4ndyandEvgeny Poberezkin 4bbadffa37 agent: api to reconnect single server (#1198)
* agent: api to reconnect single server

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-16 06:27:25 -07:00
Evgeny PoberezkinandAlexander Bondarenko 388d77b61a ntf stats on SMP server (#1197)
* log undelivered notifications

* type

* add counters and encoding

* rename

* diff

* style

* style2

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-06-15 20:05:45 -07:00
Alexander Bondarenko 6597f6f0ed tests: add proxy connection refused test (#1200) 2024-06-15 19:27:58 -07:00
M. Sarmad QadeerandEvgeny Poberezkin 7008b08031 smp-server: fix layout (#1196)
* smp-server: fix layout

* ws

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-15 12:52:41 -07:00
spaced4ndyandEvgeny Poberezkin bb1d31e459 remote ctrl: differentiate RCP error when trying to connect to unknown host identity (#1195)
* remote ctrl: differentiate RCP error when trying to connect to unknown host identity

* rename

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-06-10 17:42:51 +04:00
sh 369efc24c2 scripts: update install/update scripts (#1192) 2024-06-08 08:12:16 +01:00
spaced4ndy da620c388a xftp: start chunk upload after all chunks are prepared (#1191) 2024-06-06 15:50:15 +04:00
spaced4ndy 3c0cd7efcc agent: separate type for agent file errors (#1185) 2024-06-05 18:44:32 +04:00
Evgeny Poberezkin 3d605310ed agent: remove protocol encodings and agent TCP server (#1189)
* rfc: remove agent protocol encodings

* agent: remove protocol encodings and agent TCP server

* update

* remove unused code

* remove

* move tests

* add delay to tests

* stabilize test

* test

* more delays

* reduce delays

* enable all tests

* delays

* style
2024-06-05 14:34:40 +01:00
Evgeny Poberezkin 3dab330480 use throwE instead of throwError (#1187)
* use throwE instead of throwError

* test delay
2024-06-05 11:20:50 +01:00
Evgeny Poberezkin 44b8f265ae 5.8.0: changelog 2024-06-03 10:01:59 +01:00
Evgeny Poberezkin 2e4f507919 5.8.0.10 2024-05-31 22:20:30 +01:00
Evgeny Poberezkin d28b17e787 xftp server: send HTTP2 error as timeout error to the client so it is treated as temporary (#1186)
* xftp server: log file reception error

* report HTTP2 error as timeout error

* reduce timeout to 5 min

* process timeout error in protocol response

* log warning on timeout/HTTP2 error
2024-05-31 22:18:28 +01:00
Evgeny Poberezkin e1017e2a7f 5.8.0.9 2024-05-31 14:25:57 +01:00
Evgeny Poberezkin 1ff91d888f Merge branch 'stable' 2024-05-31 14:25:19 +01:00
Evgeny Poberezkin 0b5ab3a374 5.7.6.0 2024-05-31 14:23:21 +01:00
Evgeny Poberezkin 8ed54b33e0 agent: report correct errors from xftp handshake so they are treated as temporary (#1184)
* agent: report correct errors from xftp handshake so they are treated as temporary

* disable slow servers test

* remove comments

* all tests

* remove duplicate functions
2024-05-31 13:01:13 +01:00
Evgeny Poberezkin 3a3a84c58c server: log proxy connection errors 2024-05-31 12:20:29 +01:00
Evgeny Poberezkin d12ea92055 agent: report correct errors from xftp handshake so they are treated as temporary (#1184)
* agent: report correct errors from xftp handshake so they are treated as temporary

* disable slow servers test

* remove comments

* all tests

* remove duplicate functions
2024-05-31 09:47:47 +01:00
Evgeny Poberezkin 6e4067dc0c add string encodings for SMPProxyMode and SMPProxyFallback 2024-05-31 09:16:00 +01:00
Evgeny Poberezkinandspaced4ndy 88f1b727e0 SMP protocol extension to debug subscribed SMP queues (#1181)
* SMP protocol extension to debug subscribed SMP queues

* fix, test

* corrections

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* increase delays

* increase timeout

* delay

* delay

* enable all tests

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-05-30 18:49:43 +01:00
spaced4ndy 97a953550f agent: getAgentQueuesInfo (#1180) 2024-05-30 14:21:29 +04:00
Evgeny Poberezkin 39b3b5a25e 5.8.0.8 2024-05-29 13:19:10 +01:00
Alexander BondarenkoandEvgeny Poberezkin 15f0bb9e79 tcp-server: recover from accept errors (#1179)
* tcp-server: recover from accept errors

* log

* warn

* where

* retry

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-29 13:18:00 +01:00
5c2c88315a SMP server information (#1072)
* SMP server information

* fix tests

* country codes

* smp-server: serve contact and link pages from static files (#1084)

* smp-server: serve contact and link pages from static files

* generate index

* use params from ini

* render using ServerInformation

* tweak templates

* update

* fix some html

* smp-server: fix layout (#1097)

* smp-server: fix layout

* port fixes to link page

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>

* update server information page

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: M. Sarmad Qadeer <MSarmadQadeer@gmail.com>

* update server info

* web: improve server info page design (#1166)

* web: improve server info page design

* web: fix font errors & some tags

* web: improve contact & invitation page layout and header

* update

* remove unused files/css

* cleanup

* fix link page

* remove unused font links

---------

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

* show contact address as is

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: M. Sarmad Qadeer <MSarmadQadeer@gmail.com>
2024-05-29 11:30:42 +01:00
Evgeny Poberezkin 0f663bd569 5.8.0.7 2024-05-29 08:09:27 +01:00
Evgeny Poberezkin 63f5e76f9c agent: treat absent proxy session as a temporary error to retry sending (#1178)
* agent: treat absent proxy session as a temporary error to retry sending

* enable all tests
2024-05-29 08:06:01 +01:00
Evgeny Poberezkin ee052a454e 5.8.0.6 2024-05-28 15:33:55 +01:00
Evgeny Poberezkin e55ec07fe2 server: log stats for QUOTA and other errors (#1177)
* server: log stats for QUOTA errors

* fix test

* more stats

* remove duplicate column
2024-05-28 15:32:41 +01:00
Evgeny Poberezkin 199f85ec62 agent: send MWARN on QUOTA errors (#1176)
* agent: send MWARN on QUOTA errors

* fix tests
2024-05-28 11:56:57 +01:00
Evgeny Poberezkin 4a96dbf871 server: preprocess proxy commands that will not be connecting to network to reduce concurrency, do not wait for destination relay responses before processing the next command (#1174)
* server: preprocess proxy commands that will not be connecting to network to reduce concurrency

* implementation

* tests

* increase proxy client concurrency

* simplify

* refactor

* refactor2

* rename

* refactor3

* fix 8.10.7
2024-05-28 09:38:47 +01:00
Evgeny Poberezkin c8b2bb2ae1 agent: process message sent in expired response to ACK (it will increase duplicates count) (#1175) 2024-05-28 08:35:43 +01:00
Evgeny Poberezkin ab7b350521 agent: prevent sending not-batched client commands once requests time out (#1173) 2024-05-27 14:55:04 +01:00
Evgeny Poberezkin df35c50b99 5.7.5.0 2024-05-25 11:10:14 +01:00
Evgeny Poberezkin bd67844169 5.8.0.5 2024-05-24 14:26:02 +01:00
Evgeny Poberezkin 470dc74391 ntf server: do not persist server connection errors 2024-05-24 14:25:05 +01:00
Evgeny Poberezkin 2ff5f5a832 agent: add context to CMD error (#1167)
* agent: add context to CMD error

* tests, more warnings

* fix tests

* log TBQueue sizes

* log locks

* more logs

* log sendMessagesB

* fix test

* log length

* refactor

* remove logging

* revert lock scope change

* cleanup

* add string error to A_PROHIBITED

* remove

* remove test limitations

* language
2024-05-24 14:13:01 +01:00
Evgeny Poberezkin e7a73a4c89 Merge branch 'stable' 2024-05-23 22:04:06 +01:00
Evgeny Poberezkin 6309f92c68 agent: fail if non-unique connection IDs are passed to sendMessages (#1170) 2024-05-23 22:01:57 +01:00
Evgeny Poberezkin 875a56b75a Merge branch 'stable' 2024-05-23 16:48:11 +01:00
Alexander Bondarenko 984394d906 core: remove MonadUnliftIO ExceptT orphans (#1169) 2024-05-23 16:44:00 +01:00
Alexander Bondarenko 5d38ad03af tests: add proxy stress tests (#1163)
* tests: add proxy stress tests

* organize benches

* add agent tests

* move prints to logNote

* fix stuck agent tests
2024-05-23 15:34:25 +01:00
Alexander Bondarenko f6bb105536 utils: remove MonadError usage (#1168) 2024-05-23 12:47:30 +01:00
Evgeny Poberezkin 6c86aa302f 5.8.0.4 2024-05-22 16:13:26 +01:00
Evgeny Poberezkin 2b09ada392 agent: reduce interval for storing server connection errors to 3 seconds 2024-05-22 14:36:54 +01:00
Evgeny Poberezkin 8097df5540 agent: persist server connection error (#1165)
* agent: persist server connection

* comment, refactor

* fix tests, reduce interval for ntf server

* cleanup

* 0
2024-05-22 13:25:49 +01:00
Evgeny Poberezkin 5bff2f5537 Merge branch 'stable' 2024-05-21 23:42:49 +01:00
Evgeny Poberezkin 769e54db76 5.7.4.1 2024-05-21 22:58:58 +01:00
Evgeny Poberezkin f50589b31a agent: remove external timeout to resubscribe (#1164)
* agent: remove external timeout to resubscribe

* liftIO

* fix tests
2024-05-21 22:52:22 +01:00
Evgeny Poberezkin 2f2a3acdc0 5.8.0.3 2024-05-21 14:51:11 +01:00
Alexander BondarenkoandEvgeny Poberezkin f50fa5c60b smp-server: limit concurrency in proxy command processing (#1162)
* smp: put client proxy command processing threads under a shared semaphore

* add LIMITS.max_proc_threads to server config

* rename to PROXY.client_concurrency

* retry on strictly greater than max concurrency

* set default to 16

* rename

* fix limit

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-21 12:53:33 +01:00
Evgeny Poberezkin e3f5d244c1 5.8.0.2 2024-05-20 17:31:08 +01:00
Alexander BondarenkoandEvgeny Poberezkin f89d715a99 smp server: add proxy stats (#1157)
* smp-server: add proxy counters

* count simplex.im messages

* update

* fix

* get own servers from INI

* remove export

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-20 17:07:33 +01:00
Alexander BondarenkoandEvgeny Poberezkin 8fe18c4f6d core: use catMaybes to collect optional replies (#1161)
* core: use catMaybes to collect optional replies

* style

* rollback agent changes and add a note

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-20 15:48:21 +01:00
spaced4ndy 2e5433676e xftp: check proxy before downloading from unknown server (#1102)
* xftp: check proxy before downloading from unknown server

* corrections

* remove import

* tests

* comment

* tests

* don't wrap into internal error

* fix tests
2024-05-20 14:14:04 +01:00
Evgeny Poberezkin 8b21f7ef2a agent: aggregate multiple expired subscription responses into a single UP event (#1160)
* agent: aggregate multiple expired subscription responses into a single UP event

* clean up

* refactor processing of expired responses

* refactor

* refactor 2

* refactor unexpectedResponse
2024-05-20 07:56:51 +01:00
Evgeny Poberezkin 7a15ea59c9 Merge branch 'stable' 2024-05-19 07:58:00 +01:00
Evgeny Poberezkin 1bb6a5c43b agent: do not increase network activity interval while offline (#1159)
* agent: do not increase network activity interval while offline

* test
2024-05-19 07:50:47 +01:00
Evgeny Poberezkin 1116aeeea1 5.8.0.1 2024-05-17 15:39:20 +01:00
Evgeny Poberezkin 077ca9c046 Merge branch 'stable' 2024-05-17 15:38:06 +01:00
Evgeny Poberezkin 33f6d2f1da agent: optimize waiting for user network to avoid contention for state updates from multiple threads (#1155)
* haskell magic

* update

* agent: optimize waiting for user network with TChan

* clean up

* Int64

* use TVar

* cleanup

* fix

* testing

* update
2024-05-17 15:12:05 +01:00
spaced4ndy b33e8f4370 agent: reconnect xftp clients (#1156) 2024-05-17 14:23:59 +01:00
Alexander Bondarenko 426f47c805 smp: use session vars for reconnecting small agent (#1152)
* smp: use session vars for reconnecting small agent

* process errors

* split session and protocol functions

* add active flag to agent

* actually invoke agent shutdown

* close proxy agent too

* restore stopping ntf subscribers
2024-05-16 19:06:27 +01:00
Evgeny Poberezkin 9af3297997 Merge pull request #1151 from simplex-chat/ep/remember-error
proxy: remember server connection error for some time
2024-05-15 21:12:52 +01:00
Evgeny Poberezkin 16d79da73e config 2024-05-15 15:15:59 +01:00
Evgeny Poberezkin 6d19033375 proxy: remember server connection error for some time 2024-05-15 12:06:42 +01:00
Evgeny Poberezkin 2d2cc86bd8 Merge pull request #1095 from simplex-chat/proxy
SMP proxy feature branch
2024-05-14 21:35:51 +01:00
Evgeny Poberezkin f51cf1deac agent: use MVar for DB connection for more fair connection distribution between threads (#1147) 2024-05-14 20:04:51 +01:00
spaced4ndy 00a2dde727 Merge branch 'master' into proxy 2024-05-14 10:41:06 +04:00
Evgeny Poberezkin 762909ce33 5.8.0.0 2024-05-13 20:35:46 +01:00
Evgeny Poberezkin 512afa1e2b agent: count received duplicate messages (#1148)
* agent: count received duplicate messages

* count total too

* names

* fix

* tuple
2024-05-13 15:16:20 +01:00
Evgeny Poberezkin c4c983348f Merge branch 'master' into proxy 2024-05-13 14:13:52 +01:00
sh 969951d963 actions: ignore uploading GHC 8.10.7 binaries (#1064)
In other words, finally upload 22.04 binaries.
2024-05-13 10:46:37 +01:00
Evgeny Poberezkin 4455b8bd0e agent: do not throw exception when command is created for deleted connection (#1150)
* agent: do not throw exception when command is created for deleted connection

* convert database busy/locked to critical alert
2024-05-13 08:10:40 +01:00
Evgeny Poberezkin 66c916dbb3 proxy: increase client timeout for proxied commands (#1145) 2024-05-12 21:12:01 +01:00
Evgeny PoberezkinandAlexander Bondarenko 91cc48aabe agent: do not mark subscriptions on expired sessions as active, mark delayed subscriptions as active on the same session, do not cancel sending expired commands (#1127)
* agent: do not mark subscriptions on expired sessions as active, do mark delayed subscriptions as active on the same session, SUBOK response in the next SMP protocol version

* client: prevent sub actions from zombie sessions (#1122)

* client: prevent sub actions from zombie sessions

* error handling

* add AERR to pass background errors to client

* switch to activeClientSession

* put closeClient under activeClientSession

* rename

* remove AERR, do not skip processing

* move check and state update to one transaction

* catch extra UPs

* fix

* check queue is still pending before making it active

---------

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

* do not forward agent error

* revert not expiring sending subs

* fixes

* track subscription responses better

* add pending connection

* Revert "revert not expiring sending subs"

This reverts commit 4310a69391.

* do not expire sending commands

* rename

* fix race

* function

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-05-12 17:47:08 +01:00
Evgeny Poberezkin 1339a8da11 5.7.4.0 2024-05-11 23:39:28 +01:00
Evgeny Poberezkin 103ae06d55 agent: remove critical error on subscription timeout (#1146) 2024-05-11 23:38:27 +01:00
Evgeny Poberezkin 8516b0dd5b proxy: negotiate client-relay version, include it in PFWD commands and in encrypted forwarded transmissions (#1144)
* proxy: negotiate client-relay version, include it in PFWD commands and in encrypted forwarded transmissions

* rename

* inline

* comment

* use correct server version when encoding forwarded commands
2024-05-11 17:11:28 +01:00
Evgeny Poberezkin 9f72b249b4 Merge branch 'master' into proxy 2024-05-11 10:25:35 +01:00
Evgeny Poberezkin acc7faea11 5.7.3.1 2024-05-10 22:24:00 +01:00
Evgeny PoberezkinandAlexander Bondarenko 727fd8b8f5 server: more efficient response to batched subscriptions (#1141)
* server: more efficient response to batched subscriptions

* add sndMsgQ for interleaving messages with replies

* remove redundant liftIO

* refactor

* refactor2

* rename

* fix

* diff

* remove comment

* remove comment

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-05-10 22:19:11 +01:00
Evgeny Poberezkin dc111437fd 5.7.3.0 2024-05-10 15:15:43 +01:00
Evgeny Poberezkin b7afb725fd proxy: send MWARN event to user on server version or host more errors (#1140)
* proxy: include delivery path in SENT event

* send MWARN event to user on server version or host more errors

* Revert "proxy: include delivery path in SENT event"

This reverts commit 5c476718ec.
2024-05-10 10:55:19 +01:00
Evgeny Poberezkin b48215d341 proxy: additional configuration for SOCKS proxy usage in SMP proxy client (#1138)
* proxy: additional configuration for SOCKS proxy usage in SMP proxy client

* update
2024-05-09 15:36:02 +01:00
Evgeny Poberezkin 5cafd9d5c4 server: more efficient responses to batch subscriptions (#1137)
* server: more efficient responses to batch subscriptions

* comments

* comment

* enable tests

* LogError
2024-05-09 09:20:57 +01:00
Evgeny Poberezkin 817fd77332 Merge branch 'master' into proxy 2024-05-08 23:00:53 +01:00
Evgeny Poberezkin b27f126bab include server version range in transport handle (#1135)
* include server version range in transport handle

* xftp handshake

* remove coment

* simplify

* comments
2024-05-08 23:00:00 +01:00
Evgeny Poberezkin ea21b296fd agent: reset error count and do not report errors when consequitive timeouts happen while offline (#1136)
* agent: reset error count and do not report errors when consequitive timeouts happen while offline

* refactor

* comment
2024-05-08 15:33:51 +01:00
spaced4ndy 3f57d54832 xftp: catch exceptions in chunk download (#1133) 2024-05-08 16:57:04 +04:00
Evgeny Poberezkin b40654c95d update agent to v7/v2 SMP/NTF protocol versions (#997)
* update agent to v7/v2 SMP/NTF protocol versions
2024-05-08 13:05:06 +01:00
Evgeny Poberezkin 6d471b8be2 Merge branch 'master' into proxy 2024-05-08 00:14:06 +01:00
Alexander BondarenkoandEvgeny Poberezkin 7a07076277 transport: require ALPN for extended handshakes (#1134)
* transport: require ALPN for extended handshakes

* fix 8.10 build

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-08 00:06:09 +01:00
Evgeny Poberezkin a70f492f4d proxy: fallback to direct connection if destination relay does not support proxy protocol (#1132)
* proxy: fallback to direct connection if destination relay does not support proxy protocol

* move version to TransportError, refactor
2024-05-07 13:37:40 +01:00
Alexander BondarenkoandEvgeny Poberezkin c85f6a2f0e proxy: reporting errors (#1108)
* smp-proxy: iron out errors

* treat proxy timeouts as temporary

* update errors

* proxy errors (missing encoding)

* update

* enable tests

* update

* update

* fix

* fix

* simplify

* test

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-06 22:00:42 +01:00
Evgeny Poberezkin f22daf5cb6 Merge branch 'master' into proxy 2024-05-06 15:26:11 +01:00
Evgeny Poberezkin 93fd424f86 5.7.2.0 2024-05-05 17:13:26 +01:00
Evgeny Poberezkin e13b0df539 client: remove TLS handshake timeout (#1129)
* client: remove TLS handshake timeout

* remove comment
2024-05-05 17:05:51 +01:00
Evgeny Poberezkin ee8e4067b0 agent: prepare connection record before joining to prevent race conditions (#1128)
* agent: prepare connection record before joining to prevent race conditions

* prepare connection for contact address as well

* clean up
2024-05-05 12:12:19 +01:00
Alexander BondarenkoandEvgeny Poberezkin 0e205e70ad add TRcvQueues tests (#1117)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-03 23:39:00 +01:00
Evgeny Poberezkin b586a6e90a client: removed concurrency limit when waiting for subscription results (#1126) 2024-05-03 22:16:52 +01:00
Evgeny Poberezkin 259f6fcc3b Merge branch 'stable' 2024-05-02 16:24:32 +01:00
Evgeny Poberezkin 8d8010a62a 5.7.1.0 2024-05-02 16:22:55 +01:00
Evgeny Poberezkin c5941b790b client: increase timeout for SOCKS connection, increase timeout for direct connection (#1123) 2024-05-02 16:00:58 +01:00
Evgeny Poberezkin 60403955c0 5.7.0.4 2024-05-02 16:00:35 +01:00
Evgeny Poberezkinandspaced4ndy 9e49c289b4 upgrade SMP/NTF servers to v7/v2 protocol versions (#996)
* upgrade SMP/NTF servers to v7/v2 protocol versions

* 5.6.0.0

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-05-02 16:00:03 +01:00
Evgeny Poberezkin 6f83273318 client: increase timeout for SOCKS connection, increase timeout for direct connection (#1123) 2024-05-02 15:14:01 +01:00
Evgeny Poberezkin fb5d54e0a2 Merge branch 'master' into proxy 2024-05-01 12:52:04 +01:00
Alexander BondarenkoandEvgeny Poberezkin 0d2c1d3c01 transport: reduce ping traffic (#1118)
* transport: reduce ping traffic

* make pings opt-in, enable automatically with SUB commands

* fix reduced delays

* enable pings on MSG too

* rename pingErrorCount

* check timeout counter even when not sending pings

* clean up

* reset timeout error count on any event

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-05-01 10:38:33 +01:00
Evgeny PoberezkinandAlexander Bondarenko 6d60de2429 proxy: agent implementation (#1106)
* proxy: agent implementation

* revert change

* update rfc

* test stuck subscription mock

* store proxy sessions inside SMP client var

* rename

* create and use proxy session

* tests

* return proxy in SENT event

* rename, more tests

* rename

* more tests

* remove comment

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-05-01 08:48:33 +01:00
Evgeny Poberezkin 323cdc412e 5.7.0.4 2024-05-01 00:56:33 +01:00
Evgeny Poberezkinandspaced4ndy 66eccbecb3 upgrade SMP/NTF servers to v7/v2 protocol versions (#996)
* upgrade SMP/NTF servers to v7/v2 protocol versions

* 5.6.0.0

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-05-01 00:51:08 +01:00
Evgeny Poberezkin 935d2e25df 5.7.0.3 2024-04-25 10:22:18 +01:00
Alexander Bondarenko b931c1be37 utils: remove zstd contexts (#1116) 2024-04-25 09:33:22 +01:00
Evgeny Poberezkin daa866f333 5.7.0.2 2024-04-22 18:59:33 +01:00
Evgeny Poberezkin fe28e02be7 agent: make version independent of PQ enqryption support (#1114)
* agent: make version independent of PQ enqryption support

* remove comment
2024-04-22 13:40:24 +01:00
Evgeny Poberezkin 1612a7e2c7 agent: reduce sizes for per-queue e2e encryption (#1113) 2024-04-22 09:25:03 +01:00
Evgeny Poberezkin b08314722d 5.7.0.1 2024-04-21 22:58:15 +01:00
Evgeny Poberezkin 2347b82b47 adjust timeouts and delayed response errors (#1112) 2024-04-21 22:57:07 +01:00
Evgeny Poberezkin 3d40393ae8 5.7.0.0 2024-04-20 18:20:18 +01:00
Alexander BondarenkoandEvgeny Poberezkin b98fdb672d transport: increase client timeouts, don't send command after timeout (#1110)
* transport: fix client handshake timeouts

* fix handshake timeout

* skip sending requests for timed out responses

* expose batch concurrency as PClient field

* move to NetworkConfig

* remove Request on timeout

* use record

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-20 18:17:48 +01:00
Evgeny Poberezkin 3ba3172aaf xftp: enable ALPN in XFTP server (#1109) 2024-04-20 09:15:33 +01:00
Evgeny Poberezkin 2d1609f222 update envelope sizes for proxied messages, remove unnecessary proxy-relay encryption padding (#1107)
* update envelope sizes for proxied messages

* remove unnecessary padding from proxy-relay encryption
2024-04-19 20:24:25 +01:00
Alexander BondarenkoandEvgeny Poberezkin 58ede38bf4 put smp errors into proxy wrappers (#1103)
* put smp errors into proxy wrappers

* use substring in PROXY UNEXPECTED error

* fix encoding

* revert String encoding, discard invalid errors in QC

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-19 14:58:15 +01:00
Evgeny Poberezkin 7712070cb3 Merge branch 'master' into proxy 2024-04-18 23:33:06 +01:00
Evgeny Poberezkin c00c223f3b remove (or make optional) client key from handshakes (#1104)
* remove (or make optional) client key from handshakes

* remove comment
2024-04-18 22:43:49 +01:00
a3b229f668 SMP proxy: low level client and server implementation (#1096)
* SMP proxy: low level client and server implementation

* SMP proxy: server implementation (#1098)

* wip

* PRXY command

* progress

* SMP Proxy: client-level implementation (#1101)

* buildable

* encode messages

* update pkey

* fix queue types

* wrap SEND in proxy lookup

* WIP proxy client

* WIP

* post-rebase fixes

* encode something with something

* cleanup

* update

* fix nonce/corrId in batchingTests

* WIP: dig into createSMPProxySession

* agent

* test progress

* pass the test

* parameterize transport handle with transport peer to include server certificate (#1100)

* parameterize transport handle with transport peer to include server certificate

* include server certificate into THandle

* load server chain and sign key

* fix key type

* fix for 8.10

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: IC Rainbow <aenor.realm@gmail.com>

* cleanup

* add 2-server test

* remove subsumed test

* checkCredentials for BrokerMsg

* skip batching tests

* remove userId param

* remove agent changes

---------

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

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>

* remove unused type

* icrease test timeout

* reduce transport block

* envelope sizes

* don't fork unless have proxied commands to process

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
2024-04-18 22:35:17 +01:00
Evgeny Poberezkin 8d7e0b1d09 Merge branch 'master' into proxy 2024-04-17 17:46:39 +01:00
2f43b43225 parameterize transport handle with transport peer to include server certificate (#1100)
* parameterize transport handle with transport peer to include server certificate

* include server certificate into THandle

* load server chain and sign key

* fix key type

* fix for 8.10

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
2024-04-17 17:46:22 +01:00
ad4b5b6b71 parameterize transport handle with transport peer to include server certificate (#1100)
* parameterize transport handle with transport peer to include server certificate

* include server certificate into THandle

* load server chain and sign key

* fix key type

* fix for 8.10

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
2024-04-15 13:47:48 +01:00
Evgeny Poberezkin 98eb2742bc Merge branch 'master' into proxy 2024-04-13 18:51:17 +01:00
Evgeny Poberezkin ebb75ced12 extract SessionVar from AgentClient to reuse (#1099) 2024-04-13 18:33:12 +01:00
Evgeny Poberezkin 5e783396e0 5.6.2.2: v5.6.2 2024-04-12 23:14:20 +01:00
Evgeny Poberezkin b741b65700 use online status in network information (#1094)
* use online status in network information

* test

* refactor

* flip condition

* fix
2024-04-12 22:22:24 +01:00
Alexander BondarenkoandEvgeny Poberezkin 875ddd80d6 SMP proxy: protocol (#954)
* WIP: proxy-related types

* test plan

* buildable with stubs

* add auth test

* update protocol

* fix

* update rfc

* update protocol/types

* disable test

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-12 18:04:29 +01:00
Alexander BondarenkoandEvgeny Poberezkin 036b7523a5 xftp: matrix test for new versions (#1093)
* xftp: matrix test for new versions

* manual matrix

* more, faster

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-12 13:28:16 +01:00
spaced4ndyandEvgeny Poberezkin 3b1dd6dff9 agent: notify about available quota (#1087)
* agent: notify about available quota

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-11 09:38:12 +01:00
Evgeny Poberezkin 2e44ec5083 fix message expiration test (#1092) 2024-04-11 08:52:15 +01:00
Evgeny Poberezkin fbc25b171e xftp: reply ERR AUTH when file is removed from server storage (#1091) 2024-04-10 23:02:48 +01:00
Evgeny Poberezkinandspaced4ndy 8fa9ed6317 wait for user network availability (#1085)
* ghc-options

* wait for user network availability

* test

* update

* comment

* refactor

* slow config

* line

* waitForUserNetwork in xftp and ntf workers

* refactor

* refactor with registerDelay

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-04-10 21:50:05 +01:00
Alexander Bondarenko 91cf6841e0 http2: fix client setup (#1090)
* http2: cancel client action on setup timeout

* ignore incompatible server keys
2024-04-10 21:00:39 +01:00
Evgeny PoberezkinandAlexander Bondarenko 1219446996 dispose agent instances, fix tests, (#1089)
* dispose agent instances in tests

* fix quota test

* tests: fix tests with -threaded (#1088)

* fix some tests

* match RTS opts with apps

* less verbose rts stats

* enable sqlite extended error codes

* clean up

* unfocus

* remove extendedErrorCode

It's actually setExtendedResultCodes, which isn't yet available.

* diff

---------

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

* fix switch test

* fix

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-04-10 19:34:02 +01:00
Evgeny Poberezkin b994fd9f0f use sendRequest for HTTP2 2024-04-09 21:14:10 +01:00
Alexander BondarenkoandEvgeny Poberezkin 4c20ff6d00 xftp: negotiate protocol with ALPN (#1047)
* xftp: negotiate protocol with ALPN

* add RFC

* add handshake implementation

* implement extended handshake

* enable authentication

* update rfc

* Apply suggestions from code review

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

* cleanup

* discard trailing data

* cleanup diff

* use find

* rename

* refactor

* add x509 tests

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-09 15:03:40 +01:00
spaced4ndyandEvgeny Poberezkin 791368c7be export lock function (#1073)
* export lock functions

* rename with tick

* simplify

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-04 15:37:31 +01:00
Alexander Bondarenko a4cfcfcc85 ntf-server: fix test token handling (#1083)
* use formatted logger

* fix DeviceToken parser for apns_null

* exclude PPNull from token stats
2024-04-04 14:46:40 +01:00
Evgeny Poberezkin 6bc4f6c94e 5.6.2.1 2024-04-03 10:04:41 +01:00
Evgeny Poberezkin 84b8c8417b revert base64 change (#1081) 2024-04-03 09:27:36 +01:00
Alexander Bondarenko be9a84905f servers: read stores by chunks (#1080) 2024-04-01 19:42:30 +01:00
Evgeny Poberezkin bfd532e833 5.6.2.0 2024-04-01 11:33:22 +01:00
Alexander BondarenkoandEvgeny Poberezkin a698f9a0c4 xftp-server: run expiration checks on start (#1042)
* xftp-server: run expiration checks on start

* rename

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-04-01 11:29:19 +01:00
39bb804fab xftp: fix repeated replica creation if it was in uploaded status (#1079)
* test with failing files (in progress)

* print

* add replica uploading state

* Revert "add replica uploading state"

This reverts commit 7068213aa6.

* <=

* fix

* prints

* test no redundancy

* all tests no redundancy

* revert delay

* refactor

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-04-01 13:37:35 +04:00
Evgeny PoberezkinandAlexander Bondarenko 6ded721daa remove monad typeclasses to reduce overhead (#1077)
* remove monad typeclasses to reduce overhead

* remove unliftIO

* StrictData

* inline

* optional agent port

* avoid MonadUnliftIO instance (#1078)

* avoid MonadUnliftIO instance

* simpler liftError'

* rename

* narrow down instance

* revert

---------

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

* logServer

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-03-31 20:50:35 +01:00
Evgeny Poberezkin 6c48092f5e 5.6.1.0 2024-03-28 18:23:19 +00:00
Evgeny Poberezkin 44410535fd do not pass key to control port of xftp server (#1074) 2024-03-28 18:16:36 +00:00
Alexander BondarenkoandEvgeny Poberezkin bbc9eccf4d xftp: prevent overwriting completed upload (#1063)
* xftp: prevent overwriting completed upload

* add size check for skipCommitted

* fix import

* fail on incorrect size

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-28 18:12:48 +00:00
Alexander BondarenkoandEvgeny Poberezkin ee90ea6a69 replace base64-bytestring with base64 (#1065)
* replace base64-bytestring with base64

* minify

* use bytestring-0.10 compatible fork

PR pending...

* bump base64 fork with text compat

* move compat details to modules

* switch repo

* add back module

* cleanup

* minify

* clean imports

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-27 23:35:09 +00:00
Alexander BondarenkoandEvgeny Poberezkin 5e0123313c log control port commands, authentication for control port (#1071)
* log control port commands

* auth

* add auth to xftp, config and commands

* log missing auth

* put smp save under auth

* corrections

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-26 18:04:28 +00:00
Evgeny Poberezkin 2712fbc711 add key to XFTP server control port command (#1070)
* add key to XFTP server control port command

* name
2024-03-26 09:38:28 +00:00
Evgeny Poberezkin dbaef5a0f8 fix SMP server msgCount stat (#1069) 2024-03-25 16:26:40 +00:00
Alexander BondarenkoandEvgeny Poberezkin ff1d7f2fac ntf-server: break processing transaction (#1067)
* ntf-server: break processing transaction

* make resubscribe sync

* diff

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-25 15:35:33 +00:00
Evgeny Poberezkin c5a5edadf5 rfc: relay metadata and network decentralization (#1056) 2024-03-24 08:51:35 +00:00
Evgeny Poberezkin 8496884b42 5.6.0.4 2024-03-21 15:18:11 +00:00
Evgeny Poberezkin b32259d048 notify about critical error on agent crash (#1062)
* notify about critical error on agent crash

* waitUntilActive

* disposeAgent

* fix
2024-03-21 14:54:57 +00:00
Alexander Bondarenko a48c22ea36 core: fix leak in serverDown handler (#1061) 2024-03-21 12:03:19 +00:00
Alexander Bondarenko 9eacae5492 xftp: remove duplicate check in receiveServerFile (#1057) 2024-03-20 15:50:38 +00:00
Evgeny Poberezkin 7b6b80cea2 v5.6.0-beta.3 2024-03-19 15:00:42 +00:00
Alexander Bondarenko d68ff7e22f xftp-server: set initial usedStorage from actual file records (#1045) 2024-03-19 13:53:02 +00:00
Evgeny Poberezkin 46e49fa823 xftp: return OK on duplicate upload attempt 2024-03-19 13:52:07 +00:00
Alexander BondarenkoandEvgeny Poberezkin db3bddecca xftp-server: fix receiveServerFile (#1048)
* xftp-server: fix receiveServerFile

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-19 13:34:21 +00:00
Alexander Bondarenko 097cec1c35 utils: add stateless compress1 (#1053) 2024-03-19 12:13:42 +00:00
Alexander Bondarenko ca68eca86e agent: fix leak in getChunkDigest (#1051) 2024-03-15 12:18:15 +04:00
Alexander Bondarenkoandspaced4ndy ace09cc07d xftp: force single chunk for redirect descriptions (#1050)
* xftp: force single chunk for redirect descriptions

* Update src/Simplex/FileTransfer/Client/Main.hs

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-03-15 12:08:52 +04:00
spaced4ndy 293a2ca3f1 agent: remove withStoreCtx (#1044) 2024-03-13 13:33:59 +04:00
spaced4ndy e93ea6df71 xftp: fix sending large files (#1043) 2024-03-13 13:33:43 +04:00
Evgeny Poberezkin 0aa4ae7228 v5.6.0-beta.2 2024-03-12 14:31:30 +00:00
Alexander BondarenkoandEvgeny Poberezkin 2cad0cb201 core: check ACK handling with return type (#1041)
* core: check ACK handling with return type

* fix ratchet sync

* add SQL Locked to dbBusyLoop

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-11 19:06:53 +00:00
Evgeny Poberezkin 78eb4f764f v5.6.0-beta.1 2024-03-10 19:41:06 +00:00
Evgeny Poberezkin 819ae7040d Merge pull request #1024 from simplex-chat/pq
agent: quantum-resistant double ratchet encryption
2024-03-10 19:38:44 +00:00
Evgeny Poberezkin b4c90781bb pqdr: update envelope sizes 2024-03-10 17:53:57 +00:00
Evgeny Poberezkin 851ed2d02e pqdr: more tests, pass e2e version to rcEncrypt from config (#1040)
* pqdr: more tests, pass e2e version to rcEncrypt from config

* fix
2024-03-10 13:29:03 +00:00
Evgeny Poberezkin 054b6edb14 pqdr: clean up (#1039) 2024-03-10 11:27:31 +00:00
Evgeny Poberezkin dab55e0a9b pqdr: return agent version from connection request version & PQ support check api 2024-03-08 23:13:21 +00:00
Alexander Bondarenko 8cdd49b912 core: restore Eq instances (#1038)
* core: restore Eq instances

* remove duplicates from tests
2024-03-08 13:43:33 +00:00
Alexander Bondarenko b4e55146b8 core: fix VersionRange JSON instances (#1037) 2024-03-08 10:58:45 +00:00
Evgeny Poberezkin 03d73f442f JSON instance for VersionRange (#1036) 2024-03-08 10:01:58 +00:00
Evgeny Poberezkin 8ff4c628b5 pqdr: make envelope sizes dependent on version, test enabling PQ (#1035) 2024-03-08 08:28:15 +00:00
spaced4ndy 5e23fa6cfc agent pq: connRequestPQSupport api (#1034) 2024-03-07 19:44:48 +04:00
Alexander BondarenkoandEvgeny Poberezkin dd22b5c823 core: tweak compress api (#1029)
* convert compress to a wrapper with passthrough fallback

* add compress1 for non-batched compression

* use original size as upper bound for scratch

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-07 14:17:32 +00:00
Evgeny Poberezkin 11288866f9 pqdr: refactor 2024-03-07 12:41:10 +00:00
Evgeny Poberezkin 07fa75ec49 pqdr: agent api to confirm PQ encryption support during connection handshake, fix incorrect PQ support (#1032)
* pqdr: agent api to confirm PQ encryption support during connection handshake

* fix CONF, tests

* fix REQ, tests

* remove unused
2024-03-07 08:35:40 +00:00
Evgeny Poberezkin 4ffb6a348a pqdr: use different newtypes for supporting and enabling PQ encryption in connections (#1031)
* pqdr: use different newtypes for supporting and enabling PQ encryption in connections

* rename field, fix test

* refactor
2024-03-06 21:28:03 +00:00
Evgeny Poberezkin b435a4dacb envelope sizes dependent on PQ encryption (#1028)
* envelope sizes dependent on PQ encryption (WIP)

* add "supported" flag to ratchets, update this flag on ratchet resync

* change connection PQ status on sendMessage

* comment, fix

* refactor
2024-03-06 16:38:30 +00:00
Alexander BondarenkoandEvgeny Poberezkin e04705d9c5 utils: add generic batching and compression (#1018)
* extract batchTransmissions_

* add Simplex.Messaging.Compression

* add combined compression/batching

* force NonEmpty for batching

* hide FFI and allocation related IO

* split packing

* remove batch compression, tweak API

* OCD over API

* remove Empty, extract passthrough const

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-03-06 09:10:49 +00:00
Evgeny Poberezkin b050cf5027 double ratchet versioning for post-quantum encryption (#1025)
* correctly parse new Ratchet fields when omitted

* rfc: migrating connection versions to pqdr

* update rfc

* WIP (dont commit)

* rename versions

* update ratchet version based on PQ encryption feature flag

* remove duplicate function

* synchronize ratchet, fix tests, refactor

* comments

* test

* pattern
2024-03-05 17:07:15 +00:00
Evgeny Poberezkin 52a67daea6 agent: pass PQ encryption flag separately for each message in batch APIs (#1027) 2024-03-05 11:09:07 +00:00
Evgeny Poberezkin ba1bfaa5aa Merge branch 'master' into pq 2024-03-04 20:13:01 +00:00
Evgeny Poberezkin dd2bd11584 parameterize version scopes with phantom types (#1026)
* parameterize version scopes with phantom types

* move Version to another module

* parens
2024-03-04 19:06:51 +00:00
Evgeny Poberezkinandspaced4ndy e06e22328f agent: quantum-resistant double ratchet encryption (#939)
* doc

* diff

* ratchet header

* types

* ratchet step with PQ KEM, message header with KEM

* comment

* update types, remove Eq instances, store KEM keys to database

* pqx3dh

* PQ double ratchet test

* pqdr tests pass

* fix most tests

* refactor

* allow KEM proposals from both sides

* test names

* agent API parameters to use PQ KEM

* initialize ratchet state for enabling KEM

* fix/test KEM state machine to support disabling/enabling via messages

* more tests

* diff

* diff2

* refactor

* refactor

* refactor

* refactor

* remove Maybe

* rename

* add PQ encryption status to CON, MID and MSG events and sendMessage API results

* different PQ parameter when creating connection

* rename/reorganize types for PQ encryption modes

* rename

* fix testWaitDeliveryTimeout

* rename

* rename2

* ghc8107

* rename

* increase timeouts for concurrent send/receive test

* enable all tests

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-03-03 19:40:49 +00:00
spaced4ndy 30fd4065d9 rename delete waiting delivery tests (#1022) 2024-03-03 12:56:54 +04:00
Alexander Bondarenko 246a0d10c2 xftp: raise internal upload limit to 5gb (#1020)
* xftp: raise internal upload limit to 5gb

* extract hard limit from agent
2024-03-02 18:46:05 +00:00
Evgeny Poberezkin ce78646c7f refactor creating connection record (#1021) 2024-03-02 18:27:51 +00:00
spaced4ndyandEvgeny Poberezkin 294d7ec8dd agent: delay connection deletion to finish delivery of pending messages (#1015)
* agent: delay connection deletion to finish delivery of pending messages (wip)

* fixes, test

* notify, test

* add tests

* comment

* add test

* timeout

* test timeout

* up

* more tests

* rename

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-29 18:08:58 +00:00
Evgeny Poberezkin c9ec7ea274 Merge branch 'stable' 2024-02-27 18:07:30 +00:00
Evgeny Poberezkin 5011ac2e7c Merge branch 'v5522' into stable 2024-02-27 17:58:08 +00:00
Evgeny Poberezkin 0987895926 5.5.2.2 2024-02-27 00:00:29 +00:00
2e3c961dab agent: optimize execution performance of slow memory operations during (re)subscriptions (#1013)
* agent: remove redundant set building

* tests: add tRcvQueuesTests (#1016)

* double accounting

* unit tests for invariants

* batch add queues

* ghc8107 compatibility

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-02-27 00:00:29 +00:00
spaced4ndyandEvgeny Poberezkin 050a921fbb agent: batch db operations for deleting connections and xftp files (#1009)
* agent: batch db operations for deleting connections

* batch delete rcv files

* snd files

* refactor

* refactor2

* lines

* refactor

* fix prefix path

* refactor

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-26 10:28:50 +00:00
Evgeny Poberezkin e2ec737c68 stop using public keys from table (#1014)
* stop using public keys from table

* comment
2024-02-25 16:19:04 +00:00
Alexander Bondarenko 95c0914809 agent: speed up SMP clientDisconnected (#1011) 2024-02-24 14:59:14 +00:00
Alexander BondarenkoandEvgeny Poberezkin 385463192b agent: extend worker summary (#1007)
* agent: extend worker summary with active/idle split

* simplify

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-24 13:29:36 +00:00
Evgeny Poberezkin d7394aa762 fix ntf server version range in the client 2024-02-22 19:33:14 +00:00
Evgeny Poberezkin 0d843ea4ce Merge branch 'stable' 2024-02-21 20:33:06 +00:00
Evgeny Poberezkin 04cd2aa4ad Merge pull request #1006 from simplex-chat/v5521
5.5.2.1
2024-02-21 20:07:34 +00:00
Evgeny Poberezkin a34da38966 Merge branch 'stable' into v5521 2024-02-21 19:28:30 +00:00
Evgeny Poberezkin 32c94df040 5.5.2.1 2024-02-21 14:13:07 +00:00
Evgeny Poberezkin 5c1519df7f optimize: use IntMap (#1005) 2024-02-21 11:52:24 +00:00
Evgeny PoberezkinandAlexander Bondarenko 03c24c3ada agent: fix performance leak, smp server: optimize and fix potential memory leak (#1004)
* fix agent performance leak when re-connecting clients, optimize SMP server

* let the clients remove themselves from their clientvars

* fix another test

* do not call removeSubs when AgentClient is not active

* revert some changes

* revert more, refactor

* comment

* rename

* refactor

* refactor

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2024-02-21 11:45:34 +00:00
Evgeny Poberezkin 12dac60397 v5.6.0-beta.0 2024-02-18 16:31:48 +00:00
Evgeny Poberezkin 9b38f69e7e smp server: add stats for queues deleted before and after they were secured (#1002) 2024-02-18 16:25:32 +00:00
Evgeny Poberezkin 155831ae36 tests: test notification decryption 2024-02-18 09:30:21 +00:00
Evgeny Poberezkin 89140e0ff0 use package version for all servers (#1001) 2024-02-18 08:56:35 +00:00
Evgeny Poberezkin caeeb2df9c fix protocol in NTF server error 2024-02-17 15:32:44 +00:00
Evgeny Poberezkin 51be2fea20 agent: test NTF server (#1000) 2024-02-17 14:55:40 +00:00
Evgeny Poberezkin f6acc5c240 remove test chunk after XFTP server test (#999) 2024-02-16 23:24:57 +00:00
Evgeny Poberezkin abf956d425 fix tests for new protocol versions (#998)
* fix basic auth tests

* fix batching and syntax tests

* fix test matrix
2024-02-16 20:21:15 +00:00
Alexander Bondarenko 194a7bb58e agent: remove redirect entries when deleting rcv files (#995)
* agent: remove redirect entries when deleting rcv files

* update schema dump

* use rcv_file primary key for redirect lookup

* allow multiple redirects
2024-02-16 19:49:50 +04:00
Evgeny Poberezkin c179073260 remove support for old versions (#990)
* remove support for old versions (WIP)

* fix

* updates

* use version var
2024-02-16 13:28:50 +00:00
416f1b1721 smp: command authorization (#982)
* smp: command authorization

* fix encoding, most tests

* remove old tests

* authorize via crypto_box

* extract authenticator to Crypto module

* make TransmissionAuth Maybe

* rfc

* support authenticators in NTF protocol, test matrix (no backwards compatibility yet from new clients to old servers)

* fix/add tests, add version config to "small" agent

* separate client and server versions for SMP protocol

* test batching SMP v7

* do not send session ID in each transmission

* refactor auth verification in the server, split tests

* server "warm up" fixes timing test

* uncomment SUB timing test

* comments, disable two timing tests

* rename version

* increase auth timing test failure threshold

* use different algorithms to authorize snd/rcv commands, use random correlation ID

* transport: fetch and store server certificate (#985)

* THandleParams (WIP, does not compile)

* transport: fetch and store server certificate

* smp: add getOnlinePubKey example to smpClientHandshake

* add server certs and sign authPub

* cleanup

* update

* style

* load server certs from test fixtures

* sign ntf authPubKey

* fix onServerCertificate

* increase delay before sending messages

* require certificate with key in SMP server handshake

---------

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

* remove dhSecret from THandle

* remove v8, merge all changes to one version

* parameterize THandle

* rfc: transmission ecnryption

* Revert "parameterize THandle"

This reverts commit 75adfc94fb.

* use batch syntax for ntf server commands

* separate encodeTransmission when there is no key

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* rename

* diff

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-02-16 11:45:54 +00:00
Evgeny Poberezkin 9254d8dac5 v5.5.3 2024-02-16 11:33:56 +00:00
Alexander BondarenkoandEvgeny Poberezkin 9ab34bca7d cli: add cert command to xftp and ntf servers (#991)
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-16 10:56:54 +00:00
spaced4ndy 6f62d7ff05 agent: add numRecipients parameter to send description (#993) 2024-02-15 13:24:46 +04:00
Alexander Bondarenko 7275714b8e cli: configure server paths from env (#992) 2024-02-14 20:31:06 +00:00
spaced4ndy 004597c764 agent: add index to file redirect migration (#988) 2024-02-14 18:47:39 +04:00
Alexander BondarenkoandEvgeny Poberezkin 2f7a288280 xftp: add sending and receiving via URI-encoded redirects (#968)
* xftp: add URI encoding for FileDescription

* tweak URI

* allow smaller blocks

* draft xftpReceiveFileFollow' and xftpSendFilePublic'

* add sending with redirect

* allow 64k chunks

* add migrations with redirect fields

* add test case

* fix deadlock

* revert CLI code

* WIP: working send/receive via URI

* fix field ambiguity

* cleanup

* update agent db schema

* update minimal chunk size

* add rfc

* apply suggestions from code review

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

* add createRcvFileRedirect

* extract Simplex.Messaging.ServiceScheme and reuse for files

* update db schema

* check size/digest on receive complete

* cleanup

* use SIZE/DIGEST errors for redirects too

* split digest/size errors from redirect checks

* fix redirect error encoding

* rename RedirectMeta to RedirectFileInfo

* use query encoding for file URI

* group maybe fields under RcvFileRedirect

* add extras field

* update rfc

* add extras encoding and no-redirect tests

* fix toStrict for old ghc

* extra client data in file descr URI

* remove decoded yaml file

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-13 14:08:49 +00:00
Alexander BondarenkoandEvgeny Poberezkin 57e7c8ef6b smp-server: add cert CLI command to rotate online certificate (#984)
* smp-server: add gen-online CLI command

* use CN and algo from old certificate

* add cert checks to test

* rename command

* fix test

* cert

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-12 20:17:08 +00:00
spaced4ndy 8de23c15ad agent: return ntf server in getNtfToken (#986) 2024-02-12 16:51:37 +04:00
Evgeny Poberezkin e64b6cba4b 5.5.2.0 2024-02-10 22:07:02 +00:00
Evgeny Poberezkin 189885c50d protocol: ignore tail bytes in handshake blocks to allow future extension (#983)
* protocol: ignore tail bytes in handshake blocks to allow future extension

* cleanup
2024-02-05 21:01:20 +00:00
Evgeny Poberezkin a516c2f72c 5.5.1.2 2024-02-02 08:19:02 +00:00
Evgeny Poberezkin 2ae1c9f79d agent: only retry connecting SMP clients when there are pending subscriptions (#981)
* agent: only retry connecting SMP clients when there are pending subscriptions

* fix

* remove retry on creating clients

* simplify
2024-02-01 16:17:37 +00:00
Alexander BondarenkoandEvgeny Poberezkin 24b84106a6 core: add activity tests to workers details (#980)
* core: add activity tests to workers details

* remove names

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-01 11:02:31 +00:00
Alexander BondarenkoandEvgeny Poberezkin 15bc027f23 core: fix reconnection bugs (#979)
* core: fix reconnection bugs

* untangle newProtocolClient

* refactor

* report busy clientVar error

* log error

* comments

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-02-01 10:55:50 +00:00
Alexander Bondarenko cb64dabf75 tests: fix sporadic failures from reading stats (#975) 2024-01-30 11:36:07 +00:00
Evgeny Poberezkin 7a0cd8041b 5.5.1.1 2024-01-22 14:06:52 +00:00
Evgeny Poberezkinandspaced4ndy fd4eeb36db agent: optimize expired messages query (#976)
* agent: optimize expired messages query

* schema

* fix query

* fix

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* fix

* refactor

* comment

* refactor2

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2024-01-22 14:04:57 +00:00
Evgeny Poberezkin 1e49f1c92d 5.5.1.0 2024-01-19 19:47:39 +00:00
f7cdec2f08 fix: support multiple notification servers in configuration (#971)
* tests: add trpl-keeps-server check

* add smp server switch check

* add connection test and fix withNtfServer

* Update src/Simplex/Messaging/Agent/NtfSubSupervisor.hs

Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com>

* use ntfServer from token

* rename

---------

Co-authored-by: Evgeny Poberezkin <e.poberezkin@me.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-01-19 19:42:28 +00:00
Evgeny Poberezkin 8ff89c19dc servers: do not keep stats file open during delays (#974) 2024-01-19 17:47:47 +00:00
Evgeny Poberezkin baf2c47065 agent: expire messages failed after quota exceeded after 7 days, and expire multiple messages at once (#973)
* agent: expire messages failed after quota exceeded after 7 days, and expire multiple messages at once

* fix, test

* refactor

* catch in loop
2024-01-19 17:21:25 +00:00
Alexander Bondarenko 40fa34c2d5 tests: test multiple Ntf servers (#966)
* tests: add ntf case with multiple Ntf servers

* simplify test

* fix for master

* add server switch test

* add server switch test

* add message test for ntf server switch
2024-01-17 19:47:47 +00:00
Alexander Bondarenko eb41abfb8f tests: fix random order in async commands case (#969) 2024-01-17 11:48:41 +00:00
Evgeny Poberezkin b547f34cc0 Revert "Revert "agent: do not create user record in new databases (#957)" (#963)" (#964)
* Revert "Revert "agent: do not create user record in new databases (#957)" (#963)"

This reverts commit aee9088417.

* tests

* remove logging
2024-01-17 11:01:04 +00:00
Evgeny Poberezkin f6ed4640d4 5.5.0.6 2024-01-16 17:07:15 +00:00
Evgeny Poberezkin a0b35cec4f agent: fix potential race when good client can be removed instead of bad for the same transport session (#967)
* agent: fix potential race when good client can be removed instead of bad for the same transport session

* tryAgentError

* case
2024-01-16 13:45:51 +00:00
Evgeny Poberezkin 00c4ff4a21 agent: fix race condition in subscription worker (results in stuck delivery), better error handling in batch operations (#962)
* debugging test: join connection when reply queue creation fails

* more logs

* more logs

* more logs, fixed bug

* cleanup

* catch all errors

* simplify

* comment

* remove client ID counter
2024-01-16 10:29:35 +00:00
Evgeny Poberezkin aee9088417 Revert "agent: do not create user record in new databases (#957)" (#963)
This reverts commit 9b9a0bd0df.
2024-01-14 22:28:28 +00:00
Evgeny Poberezkin 7f7a77c4eb SMP protocol: optimize batching transactions, remove Builder (#961)
* remove Builder

* fewer chunks

* remove lazy bytestrings

* optimize

* pad
2024-01-14 20:42:47 +00:00
Alexander Bondarenko cd4329f2de agent: collect worker stats (#959)
* agent: collect worker stats

* add more workers

* process asyncCmdWorkers as a Map
2024-01-12 23:38:35 +00:00
Alexander BondarenkoandEvgeny Poberezkin 68f5e189a6 smp: check for progress when resubscribing (#956)
* smp: check for progress when resubscribing

* add allowClose to reconnectServer to distinguish entry points

* resolve error todo

* make reconnect async per-session

* remove allowClose

* deregister reconnecter when it finishes

* signal/react more work explicitly

* fix restart condition

* wait for reconnecter to finish

* remove redundant reconnect locks

* rename getClientVar for expanded scope

* formatting

* remove withPending

* move pending check to tryReconnectSMPClient loop

* combine pending check and slot release transactions

* actually reserve the slot for async

* simplify

* refactor

* refactor

* use removeClientVar

* rename

* refactor

* test

* reduce MonadError scope

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-01-12 19:29:18 +00:00
Evgeny Poberezkin 17f64e1565 docs: URI schemes (#958) 2024-01-12 17:21:57 +00:00
Evgeny Poberezkin 9b9a0bd0df agent: do not create user record in new databases (#957)
* agent: do not create user record in new databases

* update
2024-01-12 16:57:57 +00:00
Evgeny Poberezkin ad8cd1d515 5.5.0.5 2024-01-09 19:44:15 +00:00
Evgeny Poberezkin ca527b4d6c use Builder for batching commands (#953)
* use Builder for batching commands

* refactor

* refactor

* increase timeout in test

* do not materialize builder

* use concat

* debug test

* debug2

* delays

* more delay, less worker delay

* remove logs

* reduce delay

* tests

* sized builder

* builder

* comment

* remove unsafe

* style

* remove fromIntegral
2024-01-08 22:54:27 +00:00
Evgeny Poberezkin 55808b0c82 5.5.0.4 2024-01-06 11:40:15 +00:00
Evgeny Poberezkin fa794d7878 agent: send errors on subscription timeouts (#937)
* agent: debug server reconnections

* update lock only if it has the same name already

* report subscription timeouts

* clean up
2024-01-05 17:36:57 +00:00
Alexander BondarenkoandEvgeny Poberezkin 34056b9d7b xftp-server: add control port (#951)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-01-05 17:36:19 +00:00
Alexander Bondarenko 87cec9ad16 control: try deleting from senders first (#950) 2024-01-05 17:07:29 +00:00
Evgeny Poberezkin 2488cf1e3a agent: restart worker when it terminated (#948)
* agent: restart worker when it terminated

* comment

* remove writeTMVar

* remove writeTMVar 2

* remove writeTMVar 3
2024-01-05 14:22:26 +00:00
Alexander BondarenkoandEvgeny Poberezkin b4495bb4f0 xftp-server: fix stats (#944)
* xftp-server: fix stats

* count file expirations

* rename

* simplify

* check files size

* store and fix fileSize check

* do

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-01-05 11:30:27 +00:00
Alexander Bondarenko 6d4834f306 core: publish sendMessagesB (#949) 2024-01-04 15:37:13 +00:00
Evgeny Poberezkin 37d30240fd 5.5.0.3 2024-01-03 20:36:54 +00:00
Alexander BondarenkoandEvgeny Poberezkin 8fd5e9f25a servers: make writeStoreLogRecord atomic (#946)
* servers: make writeStoreLogRecord atomic

* comment

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2024-01-03 20:29:09 +00:00
Alexander Bondarenko 36298f2cea smp-server: update stats (#943)
* smp-server: check queue balance in stats vs store

* smp-server: add msgExpired stats

* add msgExpired stats

* split expire/stats transactions

* count and pass msgExpired explicitly

* save/load qCount and use it for checking store
2024-01-02 21:24:17 +00:00
Evgeny Poberezkin f954c2cd17 rfc: SMP proxies key scope and other considerations (#927)
* rfc: SMP proxies key scope and other considerations

* split line
2023-12-28 23:29:51 +00:00
Evgeny Poberezkin d0588bd0ac 5.5.0.2 2023-12-27 20:35:56 +00:00
Alexander BondarenkoandEvgeny Poberezkin 7eb7bd5e81 xftp-server: add inactiveClientExpiration (#936)
* xftp-server: add inactiveClientExpiration

* fix test config

* add test

* add xftpPing

* switch to PCEUnexpectedResponse

* remove watchdog when server quits

* rename, loop

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-27 20:31:19 +00:00
Alexander Bondarenko bb4de2e63c ci: limit releases to GHC-9.6.3 (#938) 2023-12-27 15:55:42 +00:00
spaced4ndyandEvgeny Poberezkin 22e1932372 xftp, ntf: refactor, reuse workers abstractions (#935)
* xftp: refactor, reuse workers abstractions (wip, test fails)

* rename

* refactor (tests pass)

* refactor2

* linebreaks

* do

* reuse worker abstractions for notifications

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-27 13:02:08 +04:00
spaced4ndyandEvgeny Poberezkin e43e4860b9 xftp: file workers cycle through pending files based on retries count (limit number of iterations per work item to prevent stuck delivery) (#930)
* xftp: test file reception - shouldn't get stuck if file is deleted on server

* comment

* expiration test

* approach

* wip

* sort by retries in other works, revert some diff

* revert diff

* modify tests

* refactor

* refactor

* remove prints

* apply to other workers

* remove import

* comment

* refactor

* revert queue size

* fix test

* rename

* comment, correct number of retries

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-26 22:20:25 +00:00
Alexander BondarenkoandEvgeny Poberezkin efe7ce27e7 control: add delete command (#933)
* control: add delete command

* logDeleteQueue only when found

* use default StrEncoding for CPDelete arg

* move stats update from main transaction

* use size

* stabilize AUTH timing tests

* more iterations

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-26 20:20:12 +00:00
Evgeny Poberezkin 577e3cf14d agent: add server msg IDs to agent logs, disable 2 tests (#934)
* agent: race when using agent via TCP

* remove logs

* enable tests

* comments

* use different databases for different clients

* enable all tests
2023-12-26 17:50:39 +00:00
Evgeny Poberezkinandspaced4ndy 7ddeca50e4 agent: mark work items failed (#931)
* agent: mark work items failed (WIP)

* add tests, created_at

* getWorkItem for snd and rcv files

* fix

* tests

* fix

* tests

* test

* tests

* rename

* fix,refactor

* add indexes

* update schema

* do not try to get more work when resuming an existing worker

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-12-26 17:00:42 +04:00
Evgeny Poberezkin 1e15d56e92 5.5.0.1: fix migration 2023-12-23 16:06:11 +00:00
Evgeny Poberezkin 46056557f8 5.5.0.1: update .cabal 2023-12-23 14:12:20 +00:00
Evgeny Poberezkin a57066a826 5.5.0.1 2023-12-23 13:42:22 +00:00
Evgeny Poberezkin 2489333c87 agent: use DB to queue async commands and messages (#929)
* agent: use DB to queue async commands and messages

* fix message delivery, async commands dont work

* fix async commands

* remove comment

* rename

* comment

* balance agent operation

* empty lines

* balance another agent operation

* diff

* worker

* qAddress

* fix incorrect queue ID in v1

* types for queues

* fix test

* update index
2023-12-23 12:59:53 +00:00
Evgeny Poberezkin fa457d1c25 agent: avoid race condition between worker and supervisor when getting work (#928) 2023-12-22 11:12:36 +00:00
Evgeny Poberezkin 1c2604f6a3 agent: background mode for agent NSE (#924) 2023-12-22 08:59:07 +00:00
Evgeny Poberezkin 13a60d1d39 use ChaChaDRG as the source of randomness (#920)
* use ChaChaDRG as the source of randomness

* remove functions using entropy directly

* comment
2023-12-21 00:12:08 +00:00
8c250ebe19 agent: batch sending messages (#922)
* agent: batch sending messages (attempt 4)

* handle errors in batch sending

* batch attempt 5 (#923)

* attempt 5

* remove IORefs

* add liftA2 for 8.10 compat

* remove db-related zipping

* traversable

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>

* s/mapE/bindRight/

* name

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* comment

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* remove unused funcs

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-12-19 23:01:34 +00:00
Evgeny Poberezkin 7627ce6b69 5.5.0-beta.0: SMP server/agent 5.5, XFTP server 1.2, NTF server 1.7 2023-12-19 16:07:11 +00:00
Alexander BondarenkoandEvgeny Poberezkin 7c27357eb2 server: stop inactive clients without subscriptions, additional monitoring via control port (#901)
* fix thread labelling points

* add subscription thread labels

* add explicit sections for sockets/sessions/disconnects

* adjust label threads wrt. next blocking op

* WIP: emit event on server finish

So the event log wouldn't be cut short giving
false impression of unterminated sessions.

* add handshake timeout

* hack server socket stats

* trace closeTLS exceptions

* fix build

* clean up disconnectTransport

* make disconnectTransport terminating

Thus, participating in racyAny_ around it.

* trach snd/rcv time separately, log clients as CSV

* add direct client counting/stats

* add csv dump for socket threads

* guard socket threads dump from older GHC

* cut socket thread listing into socket-threads command

* store client creation time for stats

* wrap tls setup in timeout and labels

* server: expire clients without subscriptions

* fix check for subscriptions

* remove excess tracing

* move hardcoded timeouts to configs

* cleanup

* fix tests

* wrap direct queue waits in timeout

* rewrite old TCP wrappers with their SocketState variants

* add testInactiveWithSubs

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-12-19 15:50:45 +00:00
Evgeny Poberezkin 18be2709f5 use crypton instead of cryptonite (#919) 2023-12-13 09:32:44 +00:00
Evgeny Poberezkin fb113ff008 agent: include queries longer than 5ms into slow queries (was 50ms) 2023-12-12 15:34:49 +00:00
Evgeny Poberezkin f576260594 client: increase default network timeouts (#918) 2023-12-12 12:01:38 +00:00
Evgeny Poberezkin 560dc55312 agent: notify about polled message processing (for iOS notifications) (#908)
* agent: notify about polled message processing (for iOS notifications)

* optionally keep key and support re-opening database

* exports

* test that cannot reopen when created with keepKey: false

* set max number of messages to receive for a notification to 3
2023-12-11 13:03:53 +00:00
Evgeny Poberezkin a860936072 agent: do not account for delivery of stored messages for agent suspension (#916) 2023-12-05 23:14:33 +00:00
sh e7b6b5facd docker: add missing dependency (#915) 2023-12-05 11:03:03 +00:00
Evgeny Poberezkin eaf5317834 compatibility with GHC 8.10.7, narrow dependency ranges (#914)
* Revert "raise mtl version to 2.3.1 (#912)"

This reverts commit 117168ccce.

* Revert "expand dependency ranges (#911)"

This reverts commit 90a8fc91d3.

* fix test

* remove unnecessary forks

* make text version dependent on GHC version

* text

* remove stack.yaml

* bytestring

* bytestring

* hpack 0.35

* mtl
2023-12-04 00:00:50 +00:00
Alexander Bondarenko 117168ccce raise mtl version to 2.3.1 (#912)
* raise mtl version to 2.3.1

* fix test on 8.10
2023-12-02 11:43:36 +00:00
Evgeny Poberezkin 76af9ed242 remove ghc 9.6.3 from cabal.project 2023-12-01 17:51:33 +00:00
Alexander Bondarenko 90a8fc91d3 expand dependency ranges (#911) 2023-12-01 13:08:26 +00:00
Alexander BondarenkoandEvgeny Poberezkin 6bffcc8503 compatibility with GHC 8.10.7 (#906)
* build with ghc-8.10

* add 8.10 to matrix

* cleanup

* stats

* remove getField

* agent

* server

* remote

* space

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-11-29 12:53:06 +00:00
Evgeny Poberezkin 3f2e50eebd 5.4.0 2023-11-28 22:36:32 +00:00
Evgeny Poberezkin cc8b8cd10d agent: fix hanging test (#907) 2023-11-28 22:25:58 +00:00
Alexander BondarenkoandEvgeny Poberezkin febf9019e2 remote: add controller address preferences (#905)
* remote: add controller address preferences

* suppress localhost from breaking multicast discovery w/o prefs

* rewrite findCtrlAddress

* refactor

* refactor2

* add tests

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-11-28 14:12:29 +00:00
sh 2a6be894e1 docker: bump ghc version (#904) 2023-11-27 23:41:52 +00:00
Evgeny Poberezkin 8fa369f9d3 5.4.0: xftp 1.1.3, ntf 1.6.4 (#903)
* 5.4.0: xftp 1.2.0, ntf 1.7.0

* upgrade ghc in github job

* change xftp, ntf versions

* shorter predicate for exception in test
2023-11-26 22:20:42 +00:00
Evgeny Poberezkin 31f520af05 rfc: message delivery problems (#743)
* rfc: message delivery problems

* update
2023-11-26 12:02:08 +00:00
Evgeny Poberezkin 7c29e2a189 rfc: delivery proxy (#760) 2023-11-26 11:59:57 +00:00
Evgeny Poberezkin f49607e522 rfc: SMP/XFTP proxies (#844)
* rfc: SMP/XFTP proxies

* update

* update

* update

* add threat model

* correction to encryption layer

* corrections
2023-11-26 11:57:56 +00:00
Evgeny Poberezkin 757b7eec81 5.4.0.6 2023-11-21 20:50:10 +00:00
Evgeny Poberezkin 88a4cf7d90 Merge pull request #898 from simplex-chat/ab/fix-agent-leak
agent: fix leak in AgentOpState counters
2023-11-21 20:43:32 +00:00
IC Rainbow 8ac97ed700 add retry around removing tmp dir 2023-11-21 21:30:53 +02:00
IC Rainbow 79d9f41fd8 add delay to testDuplicateMessage 2023-11-21 20:10:14 +02:00
IC Rainbow 2fe408666e fix testAsyncCommandsRestore test 2023-11-21 19:54:10 +02:00
IC Rainbow 317c68a860 agent: fix leak in AgentOpState counters 2023-11-21 14:53:53 +02:00
Evgeny Poberezkin c7a7323f54 list imports 2023-11-20 12:15:09 +00:00
spaced4ndy 044eca5ac4 5.4.0.5 2023-11-20 15:55:09 +04:00
Evgeny Poberezkin 0841067132 Merge pull request #880 from simplex-chat/xrcp
xrcp: SimpleX Remote Control protocol
2023-11-18 15:32:58 +00:00
Alexander BondarenkoandEvgeny Poberezkin 40ba94ce72 remote: add multicast discovery w/ encrypted announce (#895)
* Implement multicast discovery

* replace rcConnectMulticast with explicit discoverRCCtrl

* add multicast source/invitation host check

* remove JSON encoding for multicast invitations

* add specific error for announcing "new" controllers

* rename

* set size, rename

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-11-17 17:56:14 +00:00
Alexander Bondarenko c501f4f9cc remote: fix deadlocked client when server rejects its cert (#897)
* remote: detect tls errors sooner to prevent deadlocks

* remove redundant error checking

* cleanup
2023-11-17 10:37:32 +00:00
Evgeny Poberezkin 3b348a463c Merge branch 'master' into xrcp 2023-11-14 14:36:21 +00:00
spaced4ndy 7aae6f3cbe agent: fix case of ratchet synchronization getting stuck in endless loop (#892) 2023-11-14 17:55:20 +04:00
Evgeny Poberezkin d60911c780 xftp: refactor receiving encrypted file (#896) 2023-11-14 13:51:23 +00:00
Evgeny Poberezkin e0b7942e45 remote: return tls with remote host before host confirmation (#894) 2023-11-13 19:25:53 +00:00
Alexander Bondarenko 4f5d52ada4 Do not allow rejected client certificate (#893) 2023-11-13 17:57:26 +00:00
Evgeny Poberezkin c051ebab74 Merge branch 'master' into xrcp 2023-11-11 09:37:15 +00:00
Evgeny Poberezkin 9460551a04 5.4.0.4: xftp 1.1.2, ntf 1.6.3 2023-11-11 09:16:40 +00:00
Evgeny Poberezkin 6a2e6b040e additional lazy crypto for XRCP (#890) 2023-11-10 11:16:43 +00:00
Evgeny Poberezkin bd06b47a9d return TLS session on remote connection step (#891) 2023-11-09 22:27:30 +00:00
Evgeny Poberezkin 102487bc4f xrcp: error handling (#889)
* xrcp: error handling

* rename

* dont lock on STM

* move catch

* controller error handling

* fail sooner on incorrect CA in host HELLO

* remove TODO lock session

* refactor
2023-11-08 16:34:56 +00:00
Evgeny Poberezkin 248144f3de do not use previous KEM shared secret in multicast packets and in host HELLO (#888)
* do not use previous KEM shared secret in multicast packets and in host HELLO

* simplify
2023-11-08 12:06:10 +00:00
Alexander Bondarenko ecb23c66e0 Extract public RC types (#887) 2023-11-07 20:30:28 +00:00
Alexander Bondarenko c0566d37a0 add To/FromField instance to KEMShardKey (#886) 2023-11-07 17:09:38 +00:00
Alexander BondarenkoandEvgeny Poberezkin 1a8dfb4cbe agent: xrcp implementation for connection via link (no multicast) (#884)
* WIP: start working on connection invites

startSession/OOB is broken now - the port isn't coming from the actualy assigned one.

* Add invite types

* clean old invite-related types

* apply renames

* Move SessionKeys from Invitation

* Stub host-side keys and handle

* move keys and handles to Types

* add Simplex.RemoteControl.Client

* more keys

* progress

* crypto for sessions

* progress to multicast

* multicast crypto

* add RC TLS server

* agent api for remote control

* WIP: tls client

* fix test

* update encoding to include nonce

* add TODO

* update

* Use network-info to find TransportHost

* request and submit tls client certificate

* WIP: add missing bits for testing RC client

* RCEncryptedHello encoding

* add block encoding

* refactor

* validate known host certificate

* remove some spaghetti

* functional API to host/ctrl clients

* refactor connectRCCtrl_

* refactor connectRCHost

* question

* add type

* fix RC session

* update doc

* update doc 2

* add block on confirmation

* remove unused parameter

* export CtrlSessKeys

* export

* fix parsing

* move test of xrcp handshake

* move KEM to HELLO step

* fix JSON

* type

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-11-07 13:05:18 +00:00
Evgeny Poberezkin 1a0c4b73de Merge branch 'master' into xrcp 2023-11-04 18:26:01 +00:00
Evgeny Poberezkin e9b5a849ab update http2 to 4.2.2 (#879) 2023-11-01 10:57:19 +00:00
Evgeny Poberezkin a5fed340e2 hybrid shared secret X25519 + sntrup761 (#881)
* hybrid shared secret X25519 + sntrup761

* use IORef

* enable all tests

* update

* update

* use newtype

* move withDRG
2023-11-01 10:33:15 +00:00
Evgeny Poberezkin ce796ec225 Merge branch 'master' into xrcp 2023-11-01 09:24:06 +00:00
Evgeny Poberezkin 4660ee9cff agent: revert to TVar (#883) 2023-11-01 09:15:51 +00:00
Evgeny Poberezkin 70bf665938 Merge branch 'master' into xrcp 2023-10-31 23:52:45 +00:00
Evgeny Poberezkin b5f733d2db agent: use IORef for DRG (#882) 2023-10-31 23:52:13 +00:00
Evgeny PoberezkinandIC Rainbow c22c15a2e8 sntrup761 (#865)
* add sntrup761 source

* it compiles

* Wrap bindings in non-FFI types

Test passes with a dummy RNG.

* pass ChaChaDRG via FunPtr

* Add iOS smoke test at createAgentStore

* style

* add "ssl" library dep

Attempt to fix missing _SHA512 symbol on macos.

* remove sha512 wrapper and use openssl directly

* restore names, remove dummy RNG

* Revert "remove sha512 wrapper and use openssl directly"

This reverts commit f9f7781f09.

* restore code from RFC

* shorter names

* enable all tests

* remove run test

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
2023-10-31 22:44:46 +00:00
Evgeny PoberezkinandAlexander Bondarenko db1b2f77cd rfc: SimpleX Remote Control protocol v2 (#874)
* rfc: SimpleX Remote Control protocol

* tweak rfc sections

* rfc: SimpleX Remote Control protocol v2

* add session verification

* add alternative design

* add KEM to rfc

* key agreement

* add pad

* pad

* padding

* remove marker from multicast packet

* update

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2023-10-31 19:39:42 +00:00
Alexander BondarenkoandEvgeny Poberezkin 8e2ffcb297 extract xrcp protocol (#878)
* xrcp: files and folders

* extract remote up to rev-http

* Remove unused files

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-10-31 19:31:53 +00:00
Alexander BondarenkoandEvgeny Poberezkin 0410948b56 add runTransportWith (#875)
* Cut transport server to allow custom tcp servers

Allows socket inspection before wrapping up in a transport/prototocol.

* rename

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-10-27 14:40:53 +01:00
Evgeny Poberezkin 511d793b92 derive JSON instances with TH (#869)
* derive JSON instances with TH

* defaultJSON

* remove ToJSON
2023-10-26 09:20:30 +01:00
Evgeny Poberezkin 5f750a6783 docs: amend SMP protocol transmission encoding (#843)
* docs: amend SMP protocol transmission encoding

* base64 encoding

* remove "optional"
2023-10-25 17:52:02 +01:00
spaced4ndy d920a2504b agent: check confirmed snd queue doesn't exist before replacing (#871) 2023-10-25 09:48:54 +04:00
spaced4ndy 55a6157880 agent: change invLocks key type to ByteString 2023-10-24 17:20:39 +04:00
spaced4ndy 73d7f84ee3 agent: take invitation lock on join (#870) 2023-10-24 16:55:57 +04:00
Evgeny Poberezkin cf8b9c12ff remove test dependencies (#861)
* remove test dependencies

* .hlint.yaml
2023-10-22 10:50:36 +01:00
sh 9994375c16 docker: workaround for xftp quota format (#862) 2023-10-22 09:26:36 +01:00
Evgeny Poberezkin 9d12d76078 add fourmolu (#868)
* add fourmolu

* linebreak

* simplify

* comment
2023-10-22 09:20:14 +01:00
spaced4ndy deb3fc7359 agent: order by internal id when retrieving pending messages (#867) 2023-10-19 20:17:12 +04:00
spaced4ndy 80905c0822 test concurrent send and receipts (#866) 2023-10-19 20:08:58 +04:00
Evgeny Poberezkin decf25d7dc refactor http2 file (#864)
* refactor http2 file

* move fileBlockSize to HTTP2.File
2023-10-19 10:25:21 +01:00
Evgeny Poberezkin 5e047f3199 update http2 to 4.1.4 2023-10-18 13:26:38 +01:00
spaced4ndy 1ad69cf74f agent: always parse connection request uri as simplex:/ (#863) 2023-10-16 18:25:56 +04:00
Evgeny Poberezkin 696fa652a5 agent: always return simplex:/ links in invitations 2023-10-12 14:54:37 +01:00
Evgeny Poberezkin 6b0da8ac50 export JSON tags as patterns 2023-10-07 14:21:15 +01:00
Evgeny Poberezkin 9195509485 update aeson to add tag to platform-specific single field JSON encoding (#859)
* update aeson to add tag to platform-specific single field JSON encoding

* refactor
2023-10-07 12:30:26 +01:00
Alexander BondarenkoandEvgeny Poberezkin 96a38505d6 add FromJSON instances (#856)
* Add FromJSON instances

* add missing FromJSON instances

* more JSON instances

* update comments

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-10-05 15:57:53 +01:00
Alexander BondarenkoandEvgeny Poberezkin 753a6c7542 add X509 cert and TLS credentials generator (#857)
* Add X509 cert and TLS credentials generator

* Expand Crypto toolkit and rewrite tls credentials with it

* Exclude X keys from SignatureAlgorithmX509 and TLS.PrivKey

* Add helpers for DB marshalling and fingerprints

* Derive public key from private

* remove module name from selectors

* Remove StrEncoding (PrivateKey Ed25519)

* remove comment

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-10-03 13:43:21 +01:00
Alexander Bondarenko ec1b72cb80 Run HTTP2 sessions over existing TLS transport (#852)
* Split HTTP2 server and client setup

For attaching to an existing TLS session.

* Add genTlsCredentials

* Allow chain construction from separate credentials

So the CA may be stored and leaf ephemeral.

* Rewrap X509 fingerprint into simplex KeyHash used in transport

* Fix docstring

* Remove TLS.Credentials generator
2023-09-27 17:29:39 +01:00
Evgeny Poberezkin fda1284ae4 agent: support closing and re-opening store (#855)
* agent: support closing and re-opening store

* add closed flag, tests
2023-09-27 12:50:24 +01:00
Evgeny Poberezkin 8d47f69083 agent: fix creating empty file for XFTP to close file (#851) 2023-09-21 11:57:00 +01:00
Evgeny Poberezkin 3828a9591c unpin unix package (#850) 2023-09-20 18:32:03 +01:00
Evgeny Poberezkin 81385e39bf Merge branch 'master-ghc9' 2023-09-18 21:18:45 +01:00
Evgeny Poberezkin fee897f367 fix unix to 2.8.1.1 2023-09-18 20:59:02 +01:00
sh f2d832fe20 Dockerfile: update cabal and ghc versions (#848) 2023-09-18 13:27:18 +03:00
Evgeny Poberezkin 57d08c0e36 Merge branch 'master' into master-ghc9 2023-09-17 22:30:46 +01:00
Evgeny Poberezkin 00b48babf3 5.4.0.3: ntf 1.6.2 2023-09-17 22:23:00 +01:00
Evgeny Poberezkin 0b488ceb66 ntf server: faster restoring from store log (#846)
* ntf server: faster restoring from store log

* only log sub status when changed
2023-09-17 15:16:24 +01:00
Evgeny Poberezkin 887ccbcf6c Merge branch 'master' into master-ghc9 2023-09-10 18:00:33 +01:00
Evgeny Poberezkin 0cabe0690b 5.4.0.2: xftp 1.1.1, ntf 1.6.1 2023-09-10 17:44:37 +01:00
84ce037867 extend SMP protocol to allow creating new queues without subscriptions (#839)
* Trace auto-subs flag

* Replace Bools with SubscriptionMode

* Handle SMOnlyCreate

* Wire remaining todos

* Update tests and fix

* Bump protocol level

* Apply suggestions from code review

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Scrub needs_sub from agent DB

* Scrub a few more needSubs from the agent api

* change API, fix test

* agent: do not subscribe to queue when creating reply queue

* fix encoding

* WIP: SMOnlyCreate test

* Add SM guard for confirmQueue

Allows the test case to pump the allowConnection
reply without getting PROHIBITED.

* Remove tracing

* add noMessages, remove unnecessary getConnectionMessage from test

* add sending messages to the test

---------

Co-authored-by: IC Rainbow <aenor.realm@gmail.com>
Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2023-09-10 17:07:19 +01:00
Evgeny Poberezkin 504465f8e2 xftp: add 64kb file chunk (#842)
* xftp: add 64kb file chunk

* disable chunk size 64kb in the agent

* revert rename
2023-09-08 13:31:11 +01:00
spaced4ndy 351f42650c export isCompatibleRange function (#841) 2023-09-06 15:42:47 +04:00
Evgeny Poberezkin 980e5c4d1e agent: add debugging info (#840) 2023-09-04 22:02:12 +01:00
Evgeny Poberezkin 17a1a911d8 import stateTVar 2023-09-01 22:24:53 +01:00
Evgeny Poberezkin 5dc3d739b2 agent: support encrypted local files (#837)
* agent: support encrypted local files

* migration, update store, api

* tests, fix

* use CF.plain
2023-08-31 22:43:58 +01:00
Evgeny Poberezkin dc65197cfd Merge branch 'master' into master-ghc9 2023-08-26 16:54:09 +01:00
Evgeny Poberezkin 4c0b8a31d2 agent: create parent folders when creating the database 2023-08-26 16:53:31 +01:00
Evgeny Poberezkin cffbd39f4e 5.4.0.1 2023-08-26 16:04:40 +01:00
Evgeny Poberezkin a67669287a Merge branch 'master' into master-ghc9 2023-08-26 16:03:10 +01:00
Evgeny PoberezkinandAlexander Bondarenko 7bdae793cb server: add control port commands for clients and ghc threads (#836)
* server: add control port commands for clients and ghc threads (#835)

* Add stats-rts control query

With supporting ghc-options that would provide the data.

* Add CPSkip command

Allows spamming empty lines a few times to clean up the view.

* server: Add CP commands to enumerate clients and threads

* style

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* use base64 encoding for session ID

* fromMaybe

* whitespace

---------

Co-authored-by: Alexander Bondarenko <486682+dpwiz@users.noreply.github.com>
2023-08-26 16:02:18 +01:00
Alexander BondarenkoandEvgeny Poberezkin 4eb3b8e113 server: add env lookups for smp server paths (#817)
* Add env lookups for smp server paths

Allows running smp-server without touching system paths.
Can be helpful for running multiple instances.

* allow empty env var values

* diff

* fix

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-08-26 15:54:26 +01:00
Evgeny Poberezkin ed05428227 Merge branch 'master' into master-ghc9 2023-08-25 14:11:51 +01:00
Evgeny Poberezkin 066d91b0f5 agent: export SubInfo 2023-08-25 09:20:12 +01:00
Evgeny Poberezkin 414b019ad4 agent: function to debug agent subscriptions (#834)
* agent: function to debug agent subscriptions

* add Show instances
2023-08-24 23:19:48 +01:00
Moritz AngermannandEvgeny Poberezkin 002f36dde0 5.4.0.0: use ghc 9.6.2 (#791)
* Add 9.6 compat

* compile with GHC9.6.2: dependencies, imports, code

* refactor typeclasses

* refactor record dot

* update cabal version

* update github actions

* update direct-sqlcipher

* 5.4.0.0

* update cabal.project

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-08-24 20:22:02 +01:00
Evgeny Poberezkin b001b748db agent: rename functions, remove unused code, stabilize tests (#830)
* agent: rename functions, stabilize tests

* test pattern match
2023-08-24 19:52:58 +01:00
Evgeny Poberezkin ec268e2006 agent: wait while agent is in foreground to continue xftp operation (#831) 2023-08-24 19:11:44 +01:00
Evgeny Poberezkin a97c7da48a changelog 2023-08-23 12:06:39 +01:00
Alexander Bondarenko fcce544488 Pin GHC version in cabal.project (#815)
Allows building without switching user-preferred version.
2023-08-23 11:59:25 +01:00
Evgeny Poberezkin a302b601ea docs: comment in the protocol doc 2023-08-21 17:41:30 +01:00
Evgeny Poberezkin 44abb90c63 agent: fix async join to avoid sending HELLO again after restart (#829)
* agent: fix async join to avoid sending HELLO again after restart

* update test

* add callstack to test

* improve test
2023-08-21 14:35:46 +01:00
Evgeny Poberezkin d09adb5cd6 agent: wait for db (Revert Revert db error busy treatments (#796) (#808)) (#828)
* Revert "Revert "db error busy treatments (#796)" (#808)"

This reverts commit 82aec2cd8f.

* update test

* combine pragmas
2023-08-21 09:06:51 +01:00
Evgeny Poberezkin b329eb9a86 agent: fix async JOIN (#827)
* agent: fix async JOIN

* refactor
2023-08-20 18:45:16 +01:00
Evgeny Poberezkin 40e6d16e48 client: streaming batched commands, refactor (#826) 2023-08-19 16:11:05 +01:00
Evgeny Poberezkin f3111f4559 client: batch while sending commands - wait for responses before sending the next batch (#825)
* client: batch while sending commands - wait for responses before sending the next batch

* fix comments

* fix tests
2023-08-18 21:02:47 +01:00
spaced4ndy cf2a17b80c agent: track count and average time for slow queries (#824) 2023-08-16 20:14:27 +04:00
spaced4ndy e586bef57a agent: add indexes to improve slow queries performance (#823) 2023-08-16 10:29:09 +04:00
Evgeny Poberezkin e2065ab352 agent: track slow SQL queries (#822)
* agent: track slow SQL queries

* add executeMany

* reduce threshold for slow queries to 50ms
2023-08-12 18:18:10 +01:00
Evgeny Poberezkin 43f283471c Merge branch 'stable' 2023-08-12 08:20:50 +01:00
Evgeny Poberezkin a1e56979b7 Merge commit 'fdbfe0e8d159f394f6eb0f5168620da8694033cd' into stable 2023-08-12 08:20:03 +01:00
Evgeny Poberezkin 6446edd1a6 Merge branch 'stable' 2023-08-12 08:19:25 +01:00
Evgeny Poberezkin 82aec2cd8f Revert "db error busy treatments (#796)" (#808)
This reverts commit 1afcefa5e7.
2023-08-09 12:24:03 +01:00
Evgeny Poberezkin fdbfe0e8d1 5.3.0.1 2023-07-22 13:21:53 +01:00
spaced4ndy e81f3b5529 add indexes (#809) 2023-07-22 13:20:49 +01:00
Evgeny Poberezkin 6314bb1706 agent: partially fix message queries, debug message times (#810)
* agent: partially fix message queries, debug message times

* fix more joins
2023-07-22 13:00:25 +01:00
Evgeny Poberezkin c54be32135 Revert "agent: expire messages on quota errors (#801)" (#807)
This reverts commit 3fee468051.
2023-07-22 09:16:06 +01:00
spaced4ndy 7e2b309450 migration to delete expired messages (#801 is suspected to have caused temporary traffic increase due to flood of non expired messages) (#806) 2023-07-20 16:15:33 +04:00
spaced4ndy 4ed40fa5d5 enable a test in CI ("should resume sending file after restart") (#800) 2023-07-17 20:33:25 +04:00
Evgeny Poberezkin de95119ca6 5.3.0.0 2023-07-15 13:35:09 +01:00
Evgeny Poberezkin 4fae7dcaee server: control port (#804)
* server: control port

* do not remove messages when saving via control port

* remove unused record fields

* fix tests
2023-07-15 13:33:00 +01:00
Evgeny Poberezkin d989d11478 server: refactor restoring store log (#803) 2023-07-15 10:37:08 +01:00
Evgeny Poberezkin 1901e96ecc server: make main SMP server queues unbounded (#802) 2023-07-14 21:07:45 +01:00
Evgeny Poberezkin 3fee468051 agent: expire messages on quota errors (#801) 2023-07-14 18:13:54 +01:00
Evgeny Poberezkinandspaced4ndy 58cb2855d2 agent: delivery receipts (#752)
* rfc: delivery receipts

* update doc

* update rfc

* implementation plan, types, schema

* migration, update types

* update types

* rename migration

* export MsgReceiptStatus, JSON encoding

* update rfc, schema

* correction

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* skeleton of the implementation

* more implementation (some tests fail)

* more code, 1 test fails

* fix encoding

* refactor

* refactor

* test, fix

* only send receipts in v3+, test

* flip condition

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* flip condition

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* agent version 4 required to send receipts

* fix test

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-07-13 22:33:48 +01:00
Evgeny Poberezkin 745a144e0c 5.2.1 (ntf 1.5.1, xftp 1.0.1) (#799) 2023-07-13 19:23:06 +01:00
Evgeny Poberezkin 4c6c436e7f resubscribe in batches in Client.Agent (#798)
* resubscribe in batches in Client.Agent

* fix test
2023-07-12 07:35:38 +01:00
Evgeny Poberezkin b61e3b5f95 server: disable transport timeout (#793) 2023-07-11 19:43:31 +01:00
Evgeny Poberezkin 58dbc197ce increase TCP timeout for clients (#797) 2023-07-11 18:54:14 +01:00
spaced4ndy 1afcefa5e7 db error busy treatments (#796) 2023-07-11 20:19:20 +04:00
Evgeny Poberezkin 532cd2f39c fix allFinally 2023-07-09 21:36:23 +01:00
Evgeny Poberezkin 2f5c646e55 test with catchError/tryError 2023-07-09 18:38:44 +01:00
Evgeny Poberezkin f76a5ca5b6 agent: catch IO errors correctly in MonadError (#795)
* agent: catch IO errors correctly in MonadError

* correction

* correction

* utils

* agentFinally to catch IO exceptions in ExceptT

* rename

* remove, inline

* rename utils

* utils unit test

* test to show catch and finally problems

* tryAllErrors

* enable all tests
2023-07-09 18:04:45 +01:00
spaced4ndy f2657f9c0b add crypto error to RSYNC event (#794) 2023-07-05 18:14:51 +04:00
Evgeny Poberezkin fe22d9b299 agent api to reconnect all servers (#792) 2023-07-04 22:54:45 +01:00
Evgeny Poberezkin 75fe28a8a6 lint 2023-07-03 17:43:56 +01:00
Evgeny Poberezkin 54dc8d42e7 5.2.0, ntf server 1.5.0 (#790) 2023-07-03 13:32:58 +01:00
spaced4ndy 0e1562deae increase connection agent version according to received messages; add version and flags to connection stats, bump currentSMPAgentVersion (#789) 2023-06-30 20:19:10 +04:00
Evgeny Poberezkin 94540a2c71 servers: add TCP timeouts to avoid memory leaks (#776)
* servers: add TCP timeouts to avoid memory leaks

* fix tests

* only use RecvTimeOut

* servers: simple timeout for TCP transport

* revert dependency change

* simplify

* simplify

* simplify 2
2023-06-30 16:22:01 +01:00
Evgeny Poberezkin 16367fcb3b make timeouts for batched functions dependent on the number of batches (#786)
* make timeouts for batched functions dependent on the number of batches

* fix

* refactor

* refactor

* change import

* refactor

* rename function

* rename

* refactor

* remove space
2023-06-30 12:26:09 +01:00
spaced4ndyandEvgeny Poberezkin 8be2505fa0 ratchet re-synchronization (#774)
* ratchet re-synchronization rfc wip

* additions

* additions, types

* fix tests

* re-sync implementation wip

* re-sync implementation 1st rev.

* test wip

* test passes

* doc

* wording

* improve doc schema

* single agreed state

* refactor (1 state variable)

* allowed -> required

* prohibit enqueue

* enqueue

* send via multiple queues

* test with server offline

* clarify errors

* rename

* more tests

* refactor

* rename AgentRKey

* rename AM_CONN_RATCHET_KEY

* more tests

* rename

* write encoded AgentRatchetInfo to AgentRatchetKey info

* move withConnLock

* refactor qDuplex

* re-create ratchet on receiving second key

* invert condition

* refactor

* simplify

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-06-30 14:17:08 +04:00
spaced4ndy a000419bd7 restore confirmation after client restart (#783) 2023-06-29 10:10:34 +04:00
spaced4ndy c8a8e2c297 differentiate agent INACTIVE error, treat as temporary (#784) 2023-06-29 10:00:53 +04:00
Evgeny Poberezkin f7d038ef20 agent: more batched commands (#782)
* agent: more batched commands

* layout
2023-06-28 19:50:11 +01:00
Evgeny Poberezkin 4a927d1ae2 ntf server: better batching and logging (#780)
* ntf server: better batching and logging

* reduce batch delay for ntf server

* comments

* 5.1.3, ntf 1.4.2

* more logging

* more logging

* split large batches, more logging

* remove some logs
2023-06-26 20:14:35 +01:00
spaced4ndy 3a74558e84 fix connection switch status db encoding (#781) 2023-06-26 21:03:46 +04:00
Evgeny Poberezkin e3fff98b1b 5.1.2, ntf server 1.4.1 2023-06-25 09:31:55 +01:00
Evgeny Poberezkinandspaced4ndy acea477aab batch subscriptions for ntf server (#778)
* return updated ConnectionStats from switchConnectionAsync (#777)

* batch ntf server subscriptions to SMP servers

* refactor

* fix filtering queues in reconnect

* test, remove repeated grouping

* enable all tests

* reduce subscriptions count

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-06-25 08:27:50 +01:00
spaced4ndy c31668ce28 return updated ConnectionStats from switchConnectionAsync (#777) 2023-06-19 14:51:14 +04:00
spaced4ndy 2efe1496d2 rename "stop switch" -> "abort switch", add canAbortSwitch to rcv queue info (#775) 2023-06-16 13:41:01 +04:00
spaced4ndyandEvgeny Poberezkin 011e1109e9 connection switch - track status, stop switch command (#764)
* connection switch improvements

* stop in transaction

* refactor

* checks in transaction

* withSwitchingRQ in transaction

* withSwitchedSQ in transaction

* refactor

* refactor

* inverse some cases

* reset snd switch progress on repeat qadd

* remove unused functions

* connection info type

* correction

* SPFinalizing phase, correctly update connection in event

* refactor

* tests

* refactor switch conn lock taking, take lock in switch stop, remove ineligible test

* comment

* corrections

* corrections

* corrections

* refactor

* remove checks in transactions, remove some statuses

* remove diff

* refactor

* refactor tests

* update connection in memory

* revert diff

* corrections

* more tests

* remove statuses, change switching queue lookup

* async queue deletion

* refactor

* lock, phases

* test status

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-06-15 11:15:46 +04:00
spaced4ndy fbf1afc9f5 run a single batch test to avoid hanging on server disconnect (#773) 2023-06-13 19:36:47 +04:00
sh 20dc00d96d install: simplify systemd service and add new helper stopscript (#771)
* install: simplify systemd service and add new helper stopscript

* README: update hash
2023-06-05 13:13:20 +04:00
sh 862aa29a6f install: some more fixes (#770) 2023-06-05 13:13:20 +04:00
sh ec6e44f034 install.sh: fixes (#765)
* install.sh: fixes

* change back to stable
2023-06-05 13:13:20 +04:00
sh 76e3863cbb install: simplify systemd service and add new helper stopscript (#771)
* install: simplify systemd service and add new helper stopscript

* README: update hash
2023-06-05 13:01:16 +04:00
sh 1c2fbdb2c5 install: some more fixes (#770) 2023-06-05 11:12:09 +04:00
sh 1822296481 install.sh: fixes (#765)
* install.sh: fixes

* change back to stable
2023-06-05 10:27:02 +04:00
spaced4ndy b747080db3 add more message delivery tests (#763) 2023-06-02 18:00:24 +04:00
tcely 20f7b538e5 Docker entrypoint fixes (#753)
* docker entrypoint: DRY store log backup

Don't Repeat Yourself

Set a variable for the full path to the source file.

Create the backup path by appending an extension from `date` output.

Also fixed quoting and switched to an `if` block.

Clean up block level variables.

* docker entrypoint: use ISO 8601 format

The previous format discards information about the local time zone.

* docker entrypoint: always use UTC

Now the format always ends in +00:00, and we can ignore that part again.

* docker entrypoint: provide the date format again

* docker entrypoint: remove time zone from the date format

* docker entrypoint: use an unambiguous date format

Present a leading zero before the month:
YYYY-0MM-DD

Both YYYY-MM-DD and YYYY-DD-MM are used by people and can be confusing in the beginning of the month.

* docker entrypoint: use appropriate quoting

Without avoiding field splitting a password containing a space changes the number of arguments being set.

* docker entrypoint: use explicit braces for custom variables

Make the intentions clear and don't assume the user knows the special cases for when variables won't be extended.

Example:
word=animal
words=mistake
echo "$words vs $word vs ${word}s"

* docker entrypoint-xftp-server: braces and quoting

* docker entrypoint-xftp-server: backup block

* docker entrypoints: explain date format in a comment

* switched from long to short option to date for POSIX

* docker entrypoint-smp-server: explain date format

* docker entrypoint-xftp-server: explain date format

* docker entrypoints: further explain format

I fixed the case to match the date format letters.

Also, use words to explain since I don't want everyone to need to read about date formats to understand.

* docker entrypoints: only quote letters

I was either going to quote the dashes too or stop quoting the colons.

Having less quotes was more readable.

* Revert "docker entrypoint: use an unambiguous date format"

This reverts commit ba2a93bad9.

* docker entrypoints: remove %F

* docker entrypoint-smp-server: remove %F

Used part of the explicit ISO 8601 format,
provided by the coreutils date invocation guide.

* docker entrypoint-xftp-server: remove %F

Used part of the explicit ISO 8601 format,
provided by the coreutils date invocation guide.
2023-05-29 14:12:23 +03:00
Evgeny Poberezkin 89caf55729 agent: ack message in case of decryption error (#759) 2023-05-27 18:28:14 +01:00
Evgeny Poberezkin c08cfb460b 5.1.1 2023-05-27 15:54:40 +01:00
Evgeny Poberezkinandspaced4ndy 03eca19d38 server: expire messages when restoring them (#758)
* server: expire messages when restoring them

* add test

* specify constructor

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* improve test

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-05-27 15:52:49 +01:00
Evgeny Poberezkin a5a3a2cbad server: increase server main queue size 2023-05-27 15:18:53 +01:00
spaced4ndy 4a3c5abf32 add with withTransaction version with logging (#757) 2023-05-26 20:33:48 +04:00
spaced4ndy 6c6f22051d check previous encrypted message hashes for duplicates on decryption errors (#756) 2023-05-17 15:42:57 +04:00
spaced4ndy d693868bc0 change implementation of time diff calculation (#754) 2023-05-15 20:26:18 +04:00
spaced4ndy 32a64b994e remove agentOperationBracket from file transfer (#755) 2023-05-15 20:23:18 +04:00
Evgeny Poberezkin a83f85dc58 5.1.0: smp 1.4.0, xftp 1.0.0 2023-05-12 22:01:32 +01:00
sh 0f7ede5eed docker+scripts: XFTP servers installation, update docs, install scripts (#750)
* docker: major overhaul with xftp support

* github: update docker actions

* install: add installation scripts

* readme: update readme to reflect latest changes
2023-05-12 10:45:04 +01:00
spaced4ndy ce64c91d5a add indexes for reading pending files (#751) 2023-05-10 15:17:26 +04:00
66177fd550 Add shumvgolove to CODEOWNERS (#747)
* CODEOWNERS: add shumvgolove for docker scripts

* CODEOWNERS: add shumvgolove for Dockerfiles

* CODEOWNERS: add shumvgolove for main scripts

* update

---------

Co-authored-by: shum <shum@liber.li>
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-05-09 20:08:17 +01:00
Evgeny Poberezkinandspaced4ndy 9d83a9c017 configure message/file expiration time in INI file (#749)
* configure message/file expiration time in INI file

* correct comment

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-05-09 20:07:42 +01:00
tcely 019db0ab91 DRY build.Dockerfile (#748)
In addition to Don't Repeat Yourself, using ENV shows the versions used in the image history.
2023-05-09 20:06:12 +01:00
spaced4ndyandEvgeny Poberezkin 8954f39425 agent: vacuum on migrations (#745)
* agent: vacuum on migrations

* vacuum after all migrations

* style

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-05-04 17:26:05 +04:00
Sam A 4d96e3700c Add GH Actions workflow to build and push Docker image (#734)
* Add GH Actions workflow to build and push Docker image on release

* Use a matrix to support building other applications

* Rename application from simplexmq to smp-server

* Remove linux/386 platform

* Remove linux/arm64 platform
2023-04-26 08:53:34 +01:00
sh eaa8221b95 docker: adjusments (#737)
* docker: workaround for cabal

* docker: use llvm-12

See: https://gitlab.haskell.org/ghc/ghc/-/issues/21936

* docker: switch to jammy
2023-04-25 11:31:15 +01:00
spaced4ndy af3f70829d xftp: remove user id from internal deletion apis (#739) 2023-04-25 12:48:10 +04:00
spaced4ndy ff8197b87b xftp: check encrypted file exists when uploading (#738) 2023-04-24 19:45:30 +04:00
sh fd96ee2840 docker: revert buildplatform (#736)
* docker: revert buildplatform

* docker: only build smp-server
2023-04-24 13:34:26 +01:00
sh 605970f6b6 docker: refactoring (#735)
* build: switch ubuntu image to lunar

* dcoker: support multi-platform builds

* docker: strip binary

* docker: multicore execution

* readme: change docker hub to smp-server
2023-04-24 11:10:05 +01:00
sh dc40c3461a docker: fix docker logs and add build arg (#733) 2023-04-23 15:29:19 +01:00
Evgeny Poberezkin 4da637a565 5.0.0 (#728)
* 5.0.0

* correction

* update
2023-04-20 13:53:09 +01:00
spaced4ndy 23cecc7b68 xftp: test file deletion restores after restart (#727) 2023-04-17 13:34:58 +04:00
Evgeny Poberezkin 9692e636ac support ipv6 (#715)
* support ipv6

* fix

* refactor

* only listen to ipv6

* refactor

* refactor 2

* 3

* fix build?

* fix release
2023-04-15 21:40:02 +01:00
Evgeny Poberezkin 2b93e0b17d report ratchet decryption errors from agent (#720)
* report ratchet decryption errors from agent

* agent crypto errors
2023-04-14 20:05:29 +01:00
spaced4ndy 9f0b9a83d6 xftp: use chunk digest as entity id for transport session (#725) 2023-04-14 13:01:14 +04:00
Evgeny Poberezkin 27cfc4c62b move assertForeground (#724) 2023-04-14 12:52:18 +04:00
spaced4ndy 5e39c47975 Merge pull request #709 from simplex-chat/xftp-send
xftp: recoverable send
2023-04-13 20:13:04 +04:00
Evgeny Poberezkinandspaced4ndy ed0f8c79a2 xftp: wait for agent in foreground in XFTP workers without delaying suspension (#721)
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-04-13 20:04:38 +04:00
spaced4ndy 29eb9e4e27 xftp: test additional recipient IDs are requested when number of recipients exceeds maximum per request (#723) 2023-04-13 20:04:23 +04:00
spaced4ndy e346a81e46 xftp: delete snd files on servers (#722) 2023-04-13 17:46:43 +04:00
spaced4ndy e6e17ec637 xftp: api to delete snd files internally, cleanup snd files, tests (#719) 2023-04-12 14:50:40 +04:00
spaced4ndy d35bd8a954 xftp: restore snd files, expire snd files in agent (#718) 2023-04-11 22:00:09 +04:00
spaced4ndy d1774e5b56 xftp: send progress events (#717) 2023-04-11 19:36:51 +04:00
spaced4ndy 9c460966b2 xftp: use recoverable send implementation instead of experimental, fix query, tests (#716) 2023-04-11 11:10:57 +04:00
spaced4ndy d0cd34989d xftp: choose server when creating chunk, retrying with different servers (#714) 2023-04-10 19:13:03 +04:00
Evgeny Poberezkin d838be773b xftp: agent client functions to upload file and add recipients (#713)
* xftp: agent client functions to upload file and add recipients

* re-read replicas
2023-04-07 19:34:51 +04:00
Evgeny Poberezkin 2edc7529f9 xftp: client function to create new chunk (#712)
* xftp: client function to create new chunk

* remove chunk digest update

* rename
2023-04-07 14:50:38 +01:00
spaced4ndy 6d9af2ec17 xftp: use fs path when encrypting file and uploading chunks (#711) 2023-04-07 16:08:31 +04:00
spaced4ndy 3533929905 xftp: transform SndFile to recipient descriptions; non optional chunk digest (#710) 2023-04-07 14:29:07 +04:00
spaced4ndy d8e60ecfdb xftp: recoverable send - spike (#707) 2023-04-07 11:19:50 +04:00
Evgeny Poberezkin 215d2414b7 update preset xftp servers 2023-04-06 23:12:52 +01:00
Evgeny Poberezkin b1489138ea xftp: fix file reception on 32 bit platforms (#708) 2023-04-06 20:40:52 +01:00
Evgeny Poberezkin 9f8db13553 xftp: agent API to set and test servers (#704)
* xftp: agent API to set and test servers

* ProtocolTestStep

* update agent API for XFTP servers

* ci: update ubuntu versions

* disable test hanging on ubuntu
2023-04-05 20:37:03 +01:00
Evgeny Poberezkin 511a97c5d0 update http2 2023-04-05 09:41:13 +01:00
Evgeny Poberezkin 44f0dd39f3 ci: disable 2 tests on linux only, switch to ubuntu 20 and 22 (#705)
* ci: disable 2 tests on linux only, switch to ubuntu 20 and 22

* fix platform name

* keep ubuntu 22 binaries

* Revert "keep ubuntu 22 binaries"

This reverts commit a1bbb12870.

* skip 1 more test

* skip 1 more test

* log os

* log os

* unconditionally skip test

* skip 1 more test in CI

* fix tests
2023-04-04 12:19:38 +01:00
spaced4ndy bdff274f56 use Int64 for time calculations (#706) 2023-04-04 14:51:13 +04:00
Evgeny Poberezkin 83364052b1 xftp: implement server command to add file recipients (#702) 2023-04-03 09:33:12 +01:00
Evgeny Poberezkin c6033e51a0 update http2 2023-04-01 16:43:41 +01:00
spaced4ndy 0f23b4ab5c xftp: re-create output file if decryption restarts (#701) 2023-03-30 16:27:47 +04:00
spaced4ndy e27c4f7b81 xftp: notify about errors on rcv files retry; process snd files errors (#700) 2023-03-30 16:26:33 +04:00
spaced4ndy b5869cf169 xftp: agent to expire rcv files older than 2 days (#699) 2023-03-28 15:23:08 +04:00
spaced4ndy 6b03876ef1 catch errors in cleanup manager (#698) 2023-03-27 18:55:20 +04:00
Evgeny Poberezkinandspaced4ndy 6a665a0833 support down migrations (#696)
* support down migrations

* test

* update

* add down migrations and tests

* get current migrations

* corrections

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* update tests

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-03-27 12:44:53 +01:00
Evgeny Poberezkin c5eb65fec8 xftp: send events for file sending/receiving progress (#697) 2023-03-27 11:44:15 +01:00
Tristan de Cacqueray 5743069021 Remove duplicated serverIdentity field (#491) 2023-03-26 12:37:31 +01:00
spaced4ndy 0df7733125 xftp: relative file paths, worker management (#694) 2023-03-22 15:34:25 +04:00
Evgeny Poberezkin d41c2bec2a store retry intervals to resume from the same interval on restart (#693)
* store retry intervals to resume from the same interval on restart

* add migration

* updates
2023-03-22 08:42:56 +00:00
spaced4ndy 7e265e5c59 Merge pull request #584 from simplex-chat/xftp
XFTP
2023-03-20 20:33:01 +04:00
spaced4ndy 2bec353eae xftp: add command to delete rcv file; agent manages save path (#692) 2023-03-20 20:08:38 +04:00
Evgeny Poberezkin 40da7e76dd import stateTVar 2023-03-18 13:57:17 +00:00
Evgeny Poberezkin 2e02ba86fd add stateTVar import 2023-03-18 13:38:20 +00:00
Evgeny Poberezkin 02eb1e9822 add stateTVar imports for compatibility with older hackage index in nix builds 2023-03-18 07:57:25 +00:00
Evgeny Poberezkin cd48c33c0d Merge branch 'master' into xftp 2023-03-17 13:30:52 +00:00
Evgeny Poberezkin 5258fe9b7e add sha512 (#691)
* add sha512

* update export title
2023-03-17 13:01:49 +00:00
Evgeny Poberezkin e33d5b5184 use different constraint for non-error functions (#690) 2023-03-17 16:01:24 +04:00
spaced4ndyandEvgeny Poberezkin 2b1a02d7d4 xftp: reconnect XFTP client on replica retry (#689)
* xftp: re-create XFTP client on replica retry

* closeXFTPSessionClient

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-03-17 09:50:49 +00:00
spaced4ndy 9255d48b69 xftp: move delay to replica (#688) 2023-03-16 18:12:38 +04:00
Evgeny Poberezkin bab689099f xftp: streaming file encryption/decryption to avoid memory spikes (#687)
* xftp: streaming file decryption to avoid memory spikes

* refactor, enable tests

* streaming encryption

* refactor
2023-03-16 13:57:21 +00:00
Evgeny Poberezkin a0eb53b891 xftp: use agent servers in experimental send, refactor decryption (#686)
* xftp: use agent servers in experimental send, refactor decryption

* fix/enable xftp send test test

* allow any order
2023-03-14 22:56:19 +00:00
spaced4ndy db120b6d2e xftp: pass save path to agent (#685) 2023-03-14 19:16:25 +04:00
+shyfire131and+shyfire131 64f5d63d2e Support for building ARM docker images (#679)
* add llvm to build stage

running docker build on Apple Sillicon would result in this error:

#14 49.39 <no location info>: error:
#14 49.39     Warning: Couldn't figure out LLVM version!
#14 49.39              Make sure you have installed LLVM between [9 and 13)

I assume it can’t find llvm/clang?

* install using unattended helper script

the current haskell is hardcoded to x86, so switch to using haskell’s official helper script which supports arm

also need to bump up setting the path, since ghcup is no longer in /usr/bin

reference: https://www.haskell.org/ghcup/guide/#continuous-integration

* add numa headers/libs

docker build (specifically haskell install step) would fail with :

#7 107.2 utils/ghc-cabal/dist-install/build/tmp/ghc-cabal: error while loading shared libraries: libnuma.so.1: cannot open shared object file: No such file or directory

I think this is because numa isn’t installed on arm instances of focal by default, but it is in later versions

anyway this fixes that

* also add numa to final

The image builds at this point! But it fails to boot on arm - again with a numa error.

This fixes that.

* don’t specify llvm version

---------

Co-authored-by: +shyfire131 <shyfire131@shyfire131.net>
2023-03-13 20:00:34 +00:00
Evgeny Poberezkinandspacedandy ddc2da8fe4 xftp: agent API for xftp commands and events (#684)
* xftp: agent API for xftp commands and events

* fix tests

* fix tests 2

* xftp: update agent send api

* update API to make temp path optional

* revert tmp path changes (fixes send)

---------

Co-authored-by: spacedandy <8711996+spaced4ndy@users.noreply.github.com>
2023-03-13 09:04:23 +00:00
Evgeny Poberezkin 3fe1f3c822 rename FileParty constructors 2023-03-11 10:05:07 +00:00
Evgeny Poberezkin 448b674fb1 5.0.0-beta.5 2023-03-10 17:37:38 +00:00
spaced4ndy cf1cd75a15 xftp: fix experimental send api description paths (#683) 2023-03-10 21:06:09 +04:00
Evgeny Poberezkin 40164ff21f update http2 to 4.1 (#680) 2023-03-10 16:59:26 +00:00
spaced4ndy c1266d55a4 xftp: experimental send api wip (#682) 2023-03-10 20:43:10 +04:00
spaced4ndy cf8f78f518 xftp: add random bytestring entity id for rcv files (#681) 2023-03-10 20:32:37 +04:00
Evgeny Poberezkin accfee5a52 5.0.0-beta.4 2023-03-10 10:32:05 +00:00
spaced4ndy cc2066473c xftp: cleanup complete & error files tmp paths; remove save path before decrypting if exists (#677) 2023-03-10 14:26:34 +04:00
Evgeny Poberezkin 8fde8e1344 xftp: agent command entities (#676)
* xftp: agent command entities

* progress event

* parameterize agent command by entity

* refactor

* Eq instance for APartyCmdTag
2023-03-10 09:10:52 +00:00
spaced4ndy deec963de8 xftp: agent error handling (#675)
- chunk download doesn't loop on permanent errors
- decryption errors are considered permanent - local worker doesn't retry
- update replica retries; to do - consider use for this field, or remove it
- rcv file Error status - to prevent repeat reads of chunks for download, files for decryption; also plan to use it for filtering on cleanup
- error string saved in separate field for debugging (not part of status type)
- agent event for rcv file errors
2023-03-09 15:32:17 +04:00
Evgeny Poberezkin 552759018e xftp: server types for any protocol (#672) 2023-03-09 09:54:31 +00:00
spaced4ndy 6d52150672 xftp: agent send file design wip (#674) 2023-03-08 14:30:37 +04:00
spaced4ndy e9f949f117 xftp: resume file reception and decryption on agent restart (#673) 2023-03-07 13:29:28 +04:00
spaced4ndy a0c86158d9 xftp agent: update chunk delay (#671) 2023-03-03 20:39:15 +04:00
spacedandy cc38deb65d Merge branch 'master' into xftp 2023-03-03 19:16:12 +04:00
spaced4ndyandEvgeny Poberezkin cf147397a4 xftp: agent receive file (#637)
* xftp: agent receive file draft

* receiveFile more stubs, changes to types, schema

* cabal file

* comments

* xftp_server_id

* schema changes, get client, local worker/action

* agent env, save file description, adjust schema

* client stubs

* download chunk wip, store, schema, types

* remove commented code

* read file description, schema, types

* check received, decrypt

* remove pure

* todo

* add XFTP to agent client

* add user id

* agent test

* tests

* rename supervisor into agent

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-03-03 18:50:16 +04:00
Evgeny Poberezkin 2f7c115207 retryInterval functions pass current interval to the action, to resume with the same interval on restart (improve battery consumption) (#670) 2023-03-03 14:12:36 +00:00
Evgeny Poberezkin 6ed4dd1515 update http2 library 2023-03-03 11:00:10 +00:00
Evgeny Poberezkin 914240108d xftp: update http2 to remove workarounds with padding and delay (#667) 2023-03-02 16:00:46 +00:00
Evgeny Poberezkin e764a51fc0 v5.0.0-beta.3 2023-03-01 14:37:59 +00:00
Evgeny Poberezkin 94de01a06f xftp: use SHA256 digest for file chunks to reduce file description size (#666)
* xftp: use SHA256 digest for file chunks to reduce file description size

* enable all tests
2023-03-01 14:36:55 +00:00
Evgeny Poberezkin faec32dc5e xftp: constrain supported file size to 1gb 2023-03-01 13:50:08 +00:00
Evgeny Poberezkinandspaced4ndy 6227215c3f xftp: remove file description that cannot be used (#665)
* xftp: remove file description that cannot be used

* show sender file description first

* change order

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-03-01 13:08:59 +00:00
Evgeny Poberezkinandspaced4ndy 3d3eb335e8 xftp: download timeout, reconnect CLI on timeouts, verbose logging (#664)
* xftp: download timeout, reconnect CLI on timeouts, verbose logging

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-03-01 07:47:27 +00:00
Seth For Privacy 453e18c4ad Correct minor Docker syntax error (#662)
Noticed that the environment variables for SMP server when using Docker were improperly quoted, so I corrected them.

This will fix an issue with server address being displayed incorrectly in the CLI when viewing init logs.

I also explicitly added the `:latest` tag for the Docker image in the Running example to make it more clear which tag is being run (no tag defaults to `latest`.)
2023-03-01 07:45:18 +00:00
Evgeny Poberezkin 249bcc7bb3 update cGet to not throw exception if returned string is shorter (trying to fix HTTP2), fix test (#663) 2023-02-28 19:16:35 +00:00
Evgeny Poberezkin 4afb72070a v5.0.0-beta.2 2023-02-28 09:02:05 +00:00
Evgeny Poberezkin 524c1de4de xftp: server stats (#661) 2023-02-28 08:59:28 +00:00
Evgeny Poberezkin 875a4bac5e xftp cli: use 3 chunk sizes (256kb, 1mb, 4mb) (#660)
* xftp cli: use 3 chunk sizes (256kb, 1mb, 4mb)

* hardcode default servers

* disable rand command

* hide rand command instead of removing
2023-02-27 21:15:22 +00:00
Evgeny Poberezkin f02cb2da4c 5.0.0-beta.1 2023-02-27 18:04:00 +00:00
Evgeny Poberezkin 2f15ce2662 xftp: add quota param to server CLI, restrict chunk sizes (#659)
* xftp: add quota param to server CLI

* only allow certain file sizes, fix tests
2023-02-27 18:01:18 +00:00
Evgeny Poberezkinandspaced4ndy 781f8e0000 xftp: increase timeouts for file uploads, show progress (#658)
* xftp: increase timeouts for file uploads, show progress

* refactor

* use default port 443

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* upload/download to/from chosen servers concurrently

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-02-27 13:42:15 +00:00
Evgeny Poberezkin c3872f0a55 5.0.0-beta.0 2023-02-25 19:52:33 +00:00
Evgeny Poberezkin 3e9e144145 ci: add xftp server (#657)
* ci: add xftp server

* add xftp
2023-02-25 19:51:33 +00:00
Evgeny Poberezkin 50c9c95287 Merge branch 'master' into xftp 2023-02-25 18:36:48 +00:00
spaced4ndyandEvgeny Poberezkin 1b4b184517 xftp: server - retry on duplicate ids (#655)
* xftp: server - retry on duplicate ids (with commented monad code)

* remove commented code, refactor

* replace traverse with mapM

* refactor

* refactor

* refactor

* refactor

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-02-25 17:55:58 +00:00
Evgeny Poberezkin e4aad7583f unpadded AES-GCM encryption now requires 12 bytes IV (#656)
* unpadded AES-GCM encryption now requires 12 bytes IV

* update

* simplify AuthTag encoding
2023-02-25 16:22:10 +00:00
spaced4ndy f20a996797 xftp: more store log tests (#654) 2023-02-24 18:34:01 +04:00
Evgeny Poberezkin 8c8a984c1c xftp: add basic auth (#653) 2023-02-24 17:40:26 +04:00
spaced4ndy 202922bceb xftp: server store log (#652) 2023-02-24 15:21:07 +04:00
Evgeny Poberezkinandspaced4ndy 3ab5e9d110 xftp: expire files on the server, track/limit used storage (#651)
* xftp: expire files on the server

* track/limit used storage

* support storage quota and disabling queue creation in CLI parameters

* fix ini file

* correction

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-02-24 11:09:51 +04:00
Evgeny Poberezkin 4ce4fa3423 xftp: write secret_box auth tag to the end of the file, for efficiency of ecryption/decryption (#650)
* xftp: write secret_box auth tag to the end of the file, for efficiency of ecryption/decryption

* comments
2023-02-23 18:28:20 +00:00
spaced4ndy fbf0b8b175 xftp: cli - delete file command, acknowledge received chunks (#649) 2023-02-23 16:54:50 +04:00
spaced4ndyandEvgeny Poberezkin 1f394b76a6 xftp: sender file description (#647)
* xftp: sender file description

* tests

* check, info, tests

* test

* refactor

* refactor, rename sender file

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-02-22 17:16:49 +04:00
Evgeny Poberezkinandspaced4ndy 97b8b37325 xftp: FDEL and FACK commands (#648)
* xftp: FDEL and FACK commands

* correction

* more tests

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-02-22 12:32:30 +00:00
Evgeny Poberezkin bbf9e28696 xftp: add server to client encryption layer with cryptobox (#646)
* xftp: add server to client encryption layer with cryptobox

* workarounds for http2

* update http2 commit

* remove TODOs
2023-02-22 09:03:18 +00:00
Evgeny Poberezkin 50664930cc xftp: tests with 2 servers and 2 clients 2023-02-20 12:41:15 +00:00
Evgeny Poberezkin cd1adc8307 Merge branch 'master' into xftp 2023-02-20 09:37:25 +00:00
spaced4ndy 733c9374bf fix comment (#645) 2023-02-20 11:35:01 +04:00
Evgeny Poberezkin a8121fc8ad add unpadded AES encryption 2023-02-19 12:14:48 +00:00
Evgeny Poberezkin c970674452 faster lazy secret_box 2023-02-19 10:54:54 +00:00
Evgeny Poberezkin 4a9b5412db xftp: refactor 2023-02-18 23:21:59 +00:00
Evgeny Poberezkin 1ecef81b4d xftp: validate digests of file chunks in client and server 2023-02-18 22:44:44 +00:00
Evgeny Poberezkin 2bf8cba4fd xftp: download chunks to separate files 2023-02-18 20:56:46 +00:00
Evgeny Poberezkin 628169adb4 xftp: only receive correct number of bytes 2023-02-18 19:47:17 +00:00
Evgeny Poberezkin fcef7deec0 Merge branch 'master' into xftp 2023-02-18 18:46:28 +00:00
Evgeny Poberezkin 44535628a5 4.4.1 2023-02-18 18:28:57 +00:00
Evgeny Poberezkin 6a48b735d1 increase timeout for QUOTA errors 2023-02-18 18:09:58 +00:00
Evgeny Poberezkin bd0cc25083 XFTP error type 2023-02-17 22:27:36 +00:00
Evgeny Poberezkin 15637e545b Merge branch 'master' into xftp 2023-02-17 21:40:35 +00:00
spaced4ndyandEvgeny Poberezkin c0dcf283eb server_key_hash fields (#643)
* server_key_hash fields

* test

* refactor

* fix

* order

* use sync command in test

* refactor

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-02-17 21:24:32 +00:00
Evgeny Poberezkin 2ddfb044fc parameterize protocol by error type (#644) 2023-02-17 20:46:01 +00:00
Evgeny Poberezkin acdf4f41c5 XFTP: CLI tests, output command results, info command (#642)
* XFTP: CLI tests, output command results

* info command, test

* fix test
2023-02-17 11:38:43 +00:00
spaced4ndy a3d9ee213d xftp: cli - option to pass servers, choose servers randomly (#641) 2023-02-16 21:06:23 +04:00
spaced4ndy cf7346d48b xftp: cli - verify file digest (#640) 2023-02-16 21:01:41 +04:00
spaced4ndyandEvgeny Poberezkin 4e8657097c xftp: cli client draft (#635)
* xftp: cli client draft

* more stubs

* compiles

* hash, app

* options parsers, random

* tmp

* xftp CLI client agent, simplify CLI command syntax

* only allow argument as a second parameter

* pivot signature draft

* receive file

* pivot sent chunks to recipients

* encryptFile - temp, chunks, specs

* send (upload) file and save file descriptions

* refactor, remove encrypted file

* save file size in description as string

* include filename inside padded encrypted file

* call chunk uploads concurrently, using queueing in HTTP2 as library client does not support concurrent streaming uploads

* download file (does not work yet)

* add digests to sent chunks

* fix recv - save file using AppendMode

* encrypt/decrypt sent file with secretbox

* remove print

* fix file description parsing in tests

* fix test

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-02-16 14:06:03 +04:00
Evgeny Poberezkin d8f81b5cdb Merge branch 'master' into xftp 2023-02-15 22:02:19 +00:00
Evgeny Poberezkin 2ae3100bed lazy pad/unpad, secretbox encrypt/decrypt (#639) 2023-02-15 22:01:33 +00:00
Evgeny Poberezkin 88c6b0d663 Merge branch 'master' into xftp 2023-02-15 12:51:34 +00:00
Evgeny Poberezkin 8659d4de05 NaCL secretbox based on cryptonite (#636) 2023-02-15 12:51:08 +00:00
bccef0ba47 files: server and client spike - basic upload/download (#591)
* Files: main, env, stats, storeLog

* Better + transport

* Executable

* Env

* Update Client.hs, Server.hs, and 4 more files...

* Answer on request

* Delay

* Temp file

* Bypass cert check

* update package.yml, rename

* update store log

* extend HTTP2 transport

* refactor caStore

* HTTP2 body

* update server stats

* file server/client framework

* verify server commands

* process FNEW command, CLI test works

* simple XFTP server test (fails)

* fix test, refactor

* upload chunk works

* receive file chunk in the client

* remove transport handshake

* typo

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>

* fix names

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
2023-02-13 13:36:02 +00:00
Evgeny Poberezkin d7a008985b Merge branch 'xftp' of github.com:simplex-chat/simplexmq into xftp 2023-02-12 15:53:48 +00:00
9775861b6f files: file description parsing & serialization, tests (#633)
* files: file description parsing & serialization, tests

* chunk list

* processFileDescription

* name

* different values

* undo chunk objects to strings

* parser, serializer

* use Either as monad

* refactor

* remove comments

* fix 2 tests

* fix test

* enable all tests

* remove comment

* move

---------

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2023-02-12 15:53:37 +00:00
Evgeny Poberezkin 4d333a90fa Merge branch 'master' into xftp 2023-02-10 19:07:12 +00:00
Evgeny Poberezkin eb1723acce refactor transport encoding, make client IO error "temporary" (#634)
* file server/client framework

* refactor

* make IOError temporary

* fix test
2023-02-10 19:06:28 +00:00
Evgeny Poberezkin eff501ddcc Merge branch 'master' into xftp 2023-02-09 23:10:46 +00:00
Evgeny Poberezkin b342b1dc59 extend HTTP2 transport (#632)
* extend HTTP2 transport

* refactor caStore

* HTTP2 body

* enable test

* remove maxBodySize
2023-02-09 23:09:24 +00:00
JRoberts 1523c00bcc files: file description types (#631) 2023-02-09 21:22:58 +04:00
Evgeny Poberezkin 3637d0c610 update protocol/store to include file digest 2023-02-09 09:03:34 +00:00
Evgeny Poberezkin 360e55a48f Merge branch 'master' into xftp 2023-02-08 20:57:24 +00:00
Evgeny Poberezkin ee2a764f93 agent: more strict parsing of TransportHost, tests (#629) 2023-02-04 22:59:28 +00:00
Evgeny Poberezkin cfe995325a agent: prevent batch deletions from failing on one connection sql error (#628)
* agent: prevent error reading one connection failing batched subscription

* prevent batch deletions from failing on one connection sql error

* rename
2023-02-04 20:46:45 +00:00
Evgeny Poberezkin 3be0008c5a 4.4.0 (#624)
* 4.4.0

* changelog
2023-02-03 11:29:15 +00:00
Evgeny Poberezkin 5d8febc353 avoid possible race conditions when cancelled clients/asyncs can be removed after the new ones are added (so that the new are removed as well) (#627) 2023-01-30 17:46:06 +00:00
Evgeny Poberezkin c77e7f7e43 remove tests/tmp folder after every test (#625) 2023-01-29 22:10:07 +00:00
Evgeny Poberezkin 6858f74897 Merge branch 'master' into xftp 2023-01-27 18:00:11 +00:00
Evgeny Poberezkin d1b4fa8911 Merge pull request #599 from simplex-chat/users
support multiple user profiles
2023-01-26 10:15:35 +00:00
Evgeny Poberezkin 7fb48930f6 increase database transaction timeout, add test for user deletion (#622)
* increase database transaction timeout, add test for user deletion

* more test logs
2023-01-25 20:58:03 +00:00
JRoberts ff038b492c fix DEL_RCVQ queue parameter type (#621) 2023-01-24 15:59:32 +00:00
Evgeny Poberezkin b59669a65e refactor functions operating on single connections using batch functions (#620)
* refactor functions operating on single connections using batch functions

* update tests
2023-01-24 13:14:13 +00:00
Evgeny PoberezkinandJRoberts 2ccef1690b events sent by the agent when queue, connection and user are deleted asynchronously (#619)
* events sent by the agent when queue, connection and user are deleted asynchronously

* update test

* more tests

* add user tests

* queries

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2023-01-24 11:12:07 +00:00
Evgeny Poberezkin d4fc638478 async batch connection deletion (#617)
* async batch connection deletion

* delete user record with connections, async connection deletion

* updates

* update query
2023-01-21 13:41:16 +00:00
Evgeny Poberezkin f66e8239f4 Merge branch 'master' into users 2023-01-19 17:45:31 +00:00
Evgeny Poberezkin 56eea29ec3 end SMP client connection after configured number of PING errors, reset PING error count on any success (#615)
* end SMP client connection after configured number of PING errors, reset PING error count on any success

* only terminate client on PING timeout

* refactor

* comment
2023-01-19 17:45:06 +00:00
Evgeny PoberezkinandJRoberts a854b5f2b2 batched queue deletion (#611)
* batched queue deletion

* only re-connect client on subscribe errors, not in all batch commands

* add test, fix

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

* refactor

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2023-01-19 16:53:28 +00:00
Evgeny Poberezkin 63b7956083 Merge branch 'master' into users 2023-01-18 22:20:29 +00:00
Evgeny Poberezkin 4f4b4f7eee only reconnect client on temporary errors in single subscription too (not currently used) (#616)
* only reconnect client on temporary errors in single subscription too (not currently used)

* refactor
2023-01-18 22:03:43 +00:00
Evgeny Poberezkin f9c613ffb8 Merge branch 'master' into users 2023-01-18 21:18:42 +00:00
Evgeny PoberezkinandJRoberts 14cb88e725 remove completed async action handles from memory (#614)
* remove completed async action handles from memory

* name

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2023-01-18 18:22:17 +00:00
Evgeny Poberezkin 6ccbe5e66e retry unsuccessful subscriptions in case of temporary errors (#613)
* retry unsuccessful subscriptions in case of temporary errors

* do not send DOWN if connection has any active queues
2023-01-18 14:30:25 +00:00
Evgeny Poberezkin f921dc3adb 4.3.1 2023-01-18 11:21:45 +00:00
Evgeny Poberezkin c4f377a85b fix SMP server stats (#612)
* fix SMP server stats

* add server stats test

* fix test
2023-01-18 11:07:25 +00:00
Evgeny Poberezkin ca5c4b75eb Merge branch 'master' into users 2023-01-17 20:32:23 +00:00
Evgeny Poberezkin 0af6533510 rfc: update xftp doc (#609) 2023-01-17 12:24:05 +00:00
JRoberts 42df6a421d add missing fkey indexes (#610) 2023-01-17 16:07:13 +04:00
Evgeny Poberezkin bc8acf2abf Merge branch 'master' into xftp 2023-01-16 19:29:55 +00:00
Evgeny Poberezkin 19feee881b 4.3.0 2023-01-16 12:00:37 +00:00
Evgeny Poberezkin 324e01300d execute asynchronous commands for correct users, include session in messages from SMP clients to agent (#608)
* execute asynchronous commands for correct users

* pass transport session to message processing to avoid race condition

* account for server changes when determining unused servers

* enable one test

* enable all tests, remove log
2023-01-16 11:06:19 +00:00
Evgeny Poberezkin b549910c17 support switching session mode, transport session now uses connection ID (#607)
* support switching session mode, transport session now uses connection ID

* refactor
2023-01-16 08:51:41 +00:00
Evgeny Poberezkin a1eda12475 increase sending retries on ERR QUOTA 2023-01-15 18:14:20 +00:00
Evgeny Poberezkin abee1fa70b Merge branch 'master' into users 2023-01-14 18:43:09 +00:00
Evgeny Poberezkin 701d06ba01 refactor tests to improve errors (#606)
* refactor tests to improve errors

* fix test descriptions

* fix test
2023-01-14 18:28:37 +00:00
Evgeny Poberezkin cac3432b03 Merge branch 'master' into users 2023-01-14 15:34:18 +00:00
Evgeny Poberezkin 56cc2bc71f additional SMP server stats (#605)
* additional SMP server stats

* refactor
2023-01-14 13:23:37 +00:00
Evgeny Poberezkin 9c9ba8c25c subscribe users in different sessions (#602)
* subscribe users in different sessions

* remove comments

* include userId to rcv queue map key

* use hash of userId[:entityId] as SOCKS proxy username
2023-01-13 09:58:39 +00:00
JRoberts 8e024590bc fix createUserRecord query (#604) 2023-01-13 11:31:35 +04:00
Evgeny Poberezkin f47e7bf3c5 4.2.3 2023-01-12 23:27:35 +00:00
Evgeny Poberezkin 5e31c60c35 do not show warning on server restart when restoring "quota" message and quota is exceeded (#603)
* do not show warning on server restart when restoring "quota" message and quota is exceeded

* complete case

* line break
2023-01-12 23:26:21 +00:00
Evgeny Poberezkin a0a092747a Merge branch 'master' into users 2023-01-12 16:37:59 +00:00
Evgeny Poberezkin 36bf92e54f 4.2.2 (#601) 2023-01-12 16:01:50 +00:00
Evgeny Poberezkin 1f12697279 strict writes to STM, remove type class (#600) 2023-01-12 14:59:46 +00:00
Evgeny Poberezkin 92a379e75c 4.2.1 2023-01-11 22:24:38 +00:00
Evgeny Poberezkin 0dddd59b36 increase server internal queue sizes (#595) 2023-01-11 16:14:33 +00:00
Evgeny PoberezkinandJRoberts f4ad3a983e support users in agent to isolate traffic of different users (#598)
* users table, isolate traffic sessions by users or by queues

* remove extra indices

* corrections

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2023-01-11 17:47:20 +04:00
Evgeny Poberezkin b328492dc9 4.2.0 (#597) 2023-01-09 11:16:25 +00:00
JRoberts 698b5c4146 set correct snd queue as primary on QTEST (#596) 2023-01-09 10:37:57 +00:00
Evgeny Poberezkin acfa65200a deduplicate connections in connect/disconnect responses, log errors in tPut (#593)
* remove TODO for old handshake version (this HELLO is not sent now)

* deduplicate connections in responses and verify server in the list of subscribed queues

* log transport and LargeMsg in tPut (the results it returns are only used in the tests)

* refactor

* refactor
2023-01-06 17:14:49 +00:00
Evgeny PoberezkinandJRoberts 61e0c346df simplify C.sign, Crypto tests, triage TODOs (#592)
* simplify C.sign, Crypto tests, triage TODOs

* update condition

* remove todos

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2023-01-06 15:34:05 +00:00
JRoberts 11c235a8a3 check token status when sending verification notification (#594) 2023-01-06 19:16:55 +04:00
Evgeny Poberezkin 058e3ac55e send/process "quota exceeded" message from SMP server when sender gets ERR QUOTA (#585)
* send "quota exceeded" message from SMP server when sender gets ERR QUOTA (ignored in the agent for now)

* send msg quota to the recipient to indicate that sender got ERR QUOTA, test

* switch between slow/fast retry intervals (tests do not pass yet)

* send QCONT message, refactor RetryInterval, test

* refactor

* remove comment

* remove space

* unit test for withRetryLock2

* refactor
2023-01-04 14:10:13 +00:00
Evgeny Poberezkin 470b512a88 fix CLI tests (#590) 2023-01-02 16:14:12 +00:00
Evgeny Poberezkin 8dd728e834 add XFTP to ProtocolType 2022-12-30 22:22:07 +00:00
Stanislav DmitrenkoandEvgeny Poberezkin 7adcbf839f FileStore (#589)
* FileStore

* Set instead of List

* update

* refactor

* refactor

* sort imports

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-12-30 21:07:35 +00:00
Evgeny Poberezkin 69a5896dac 4.1.0 (#588) 2022-12-29 21:33:55 +00:00
Evgeny PoberezkinandAvently 4b29855430 core: SimpleX File Transfer protocol type (#587)
* core: File transfer protocol

* Optimization

* Renaming and interfaces

* update response type

* eol

* update type

Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
2022-12-29 18:12:41 +00:00
Evgeny Poberezkin 1d78e35688 XFTP modules structure 2022-12-27 21:54:02 +00:00
Evgeny Poberezkin af903bd405 rfc: managing exceeded queue quotas (#583)
* rfc: managing exceeded queue quotas

* update
2022-12-27 16:29:07 +00:00
Evgeny Poberezkin 51b0833f98 rfc: SimpleX File Transfer protocol (#582)
* rfc: SimpleX File Transfer protocol

* update

* update

* remove space

* update transport protocol notes

* spellcheck
2022-12-27 12:42:53 +00:00
Evgeny Poberezkin 874f9f5cd6 option to enable/disable TLS handshake error logs (disable by default) (#581)
* option to enable/disable TLS handshake error logs (disable by default)

* refactor
2022-12-27 10:13:51 +00:00
Evgeny Poberezkin 66d3465c19 Merge branch 'stable' 2022-12-26 22:28:42 +00:00
sh d5bf99c32c docker: update (#578)
* readme: update for recent docker changes

* docker: update entrypoint logic

* readme: clarify
2022-12-26 22:27:56 +00:00
Evgeny Poberezkin 1d3a604988 refactor SMP client, NTF server store (#580)
* refactor SMP client, NTF server store

* remove undefined
2022-12-26 18:54:28 +00:00
Evgeny Poberezkin 267236a4f8 remove unnecessary exception handler, error format in stats 2022-12-26 13:00:41 +00:00
Evgeny Poberezkin aa17cc55c1 collect agent stats (#579)
* collect agent stats

* remove comment
2022-12-26 12:02:20 +00:00
Evgeny PoberezkinandJRoberts fb21d9836e agent: api to get hash of ratchet associated data for connection verification (#577)
* agent: api to get hash of ratchet associated data for connection verification

* enable all tests

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-12-09 09:41:12 +00:00
Evgeny PoberezkinandJRoberts e4842f4f47 agent: include server address string into BROKER errors (#575)
* agent: include server address string into BROKER errors

* eol

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-11-30 08:25:07 +00:00
Evgeny Poberezkin dc920d90d9 v4.0.0 (#573)
* v4.0.0

* update
2022-11-28 19:59:04 +00:00
Evgeny Poberezkin f53c1f5559 server: clear folders during initialization instead of deleting them (#572) 2022-11-28 07:40:59 +00:00
sh 0942bf9f1e readme: correction (#571) 2022-11-26 20:10:46 +00:00
shandEvgeny Poberezkin 57fc190a91 readme: fix install steps (#570)
* readme: fix install steps

* correction

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-11-26 14:29:59 +00:00
Evgeny PoberezkinandJRoberts 43fb513ef7 initialize SMP server interactively, CLI options (#566)
* initialize SMP server interactively, CLI options

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

* corrections

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-11-18 19:07:41 +00:00
Evgeny Poberezkin fbccca9947 refactor server CLIs (#565)
* refactor server CLIs

* refactor
2022-11-18 08:37:03 +00:00
Evgeny Poberezkin 40000047af server: refactor CLIs, tests (#564)
* server: refactor CLIs, tests

* add files, test

* rename Executable -> Main
2022-11-17 19:43:01 +00:00
Evgeny Poberezkin c2342cba05 FromJSON instance for ProtoServerWithAuth 2022-11-15 22:42:01 +00:00
Evgeny Poberezkin f3b6ed4db0 agent API for SMP server test (#563)
* agent API for SMP server test

* add JSON encoding
2022-11-15 07:50:46 +00:00
Evgeny Poberezkin 95db734b2d export BasicAuth constructor 2022-11-13 08:29:54 +00:00
Evgeny PoberezkinandJRoberts e281efdcb8 server: SMP basic auth (#561)
* server: SMP basic auth

* update ini default

* rfc, types

* tests

* update INI file

* typo

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

* refactor

* update INI options, log new queue creation mode on start (on/off/requires auth)

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-11-12 22:29:36 +00:00
Evgeny Poberezkin d2b88a1baa 3.4.0 (#560) 2022-11-09 06:33:53 +00:00
Evgeny Poberezkin 8f9e6c2112 update slow commands log (#559) 2022-11-08 21:54:27 +00:00
JRoberts f917c15b5e pin ini version to 0.4.1 (fixes server ini comment parsing) (#558) 2022-11-03 18:22:52 +04:00
Evgeny Poberezkin 374526a11b server: specialize monad (#557) 2022-11-03 11:47:28 +00:00
Evgeny Poberezkin 29b99d6716 server: additional logs for slow operations (#556)
* server: additional logs for slow operations

* more time logs
2022-11-03 13:11:53 +04:00
Evgeny Poberezkin d33cf644f5 server: split atomically in SEND (#555) 2022-11-03 08:22:20 +00:00
Evgeny PoberezkinandJRoberts 6fc3b26970 server: time/detect blocked message delivery actions (#546)
* server: log slow operations

* v3.3.1

* base-64 encode IDs

* fixing test

* log

* log

* revert

* log

* fix

* reduce timeout

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-11-02 20:13:09 +00:00
Evgeny Poberezkin 029fc6e781 add option tagSingleConstructors to sumTypeJSON 2022-11-02 09:06:47 +00:00
JRobertsandEvgeny Poberezkin d9a0e78b04 add optional client data field to ConnectionRequestUri (#551)
* add optional auxiliary data field to ConnectionRequestUri

* remove import

* fix, test

* fix StrEncoding Char

* data only in sync command, type

* fixing

* queryParamStr

* safeDecodeUtf8

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-11-01 13:42:44 +04:00
Evgeny Poberezkin 4beaafa35a add queue directions to SWITCH events (#553) 2022-11-01 08:44:32 +00:00
Evgeny Poberezkin 561649a498 add JSON instances for SwitchPhase (#552) 2022-10-31 17:09:01 +00:00
Evgeny PoberezkinandJRoberts 8d9816809f simplify queue rotation protocol (#550)
* simplify queue rotation protocol

* use simplified rotation protocol, update tests

* simplify schema

* delete all connection queues

* refactor

* switch notifications to the new queue, tests

* remove TODO

* refactor

* rfc correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

* remove duplicate set active

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-10-31 13:33:28 +04:00
Evgeny PoberezkinandJRoberts eb5c1c78cb connection queue redundancy and rotation (#521)
* rfc: queue rotation

* update rfc

* messages for queue rotation

* allow multiple subscribed queues per connection in Agent/Client.hs

* refactor

* fix module name

* allow multiple queues in duplex connection type

* update commands

* add indices

* addConnectionRcvQueue

* switch connection to another queue (WIP)

* update schema/protocol

* switching queue works, but sending messages after the switch fails

* messages are delivered after rotation

* use connection-scoped queue ID

* rename queue records fields

* refactor using SMPQueue class/instances

* simplify queries

* QKEY: check queue is not secured, refactor

* update rfc

* mark queue as primary in QUSE

* queue rotation errors

* fix async ack

* fix async ACK to send OK

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

* use SWCH command

* rename

* take into account only active queue subscription when determining connection result if at least one queue is active

* remove comment

* only enable notifications for connections with enableNtfs = True

* async test (WIP)

* async queue rotation test

* simplify combining results

* test with 2 servers

* fix unused subscribeConnection

* switch to cabal build

* increase build timeout

* increase delay in async test

* skip queue rotation tests

* build matrix

* step name

* use ubuntu-18.04 in build matrix

* enable rotation tests

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-10-29 18:57:01 +01:00
Evgeny Poberezkin 19aef52135 debug agent locks 2022-10-22 16:43:14 +01:00
Evgeny Poberezkin d06bb2b9c5 use KDF in X3DH (#548)
* use KDF in X3DH

* configure ratchet version via agent config, test matrix

* add comment
2022-10-20 11:03:19 +01:00
Evgeny Poberezkin f43e5d6833 refactor test matrix (#549) 2022-10-20 10:13:33 +01:00
Evgeny PoberezkinandJRoberts 7f81396b09 prevent pad/unpad failures on large/small messages (#547)
* prevent pad/unpad failures on large/small messages

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-10-19 20:53:29 +01:00
JRoberts f97c1a7712 agent: acceptContactAsync functional api (#545) 2022-10-13 19:46:32 +04:00
Evgeny Poberezkin 10e0e58ec3 improve concurrency by not holding connection lock while async command retries (#544)
* improve concurrency by not holding connection lock while async command retries

* fix

* debug test
2022-10-09 18:00:42 +01:00
Evgeny Poberezkin 0fa40fbfb0 fix: connection-level locks (#543)
* fix: connection-level locks

* add context to store error

* convert SERatchetNotFound to CONN NOT_FOUND
2022-10-04 14:36:51 +01:00
Evgeny Poberezkin f4baa5d672 increase concurrency with per-connection locks (#542)
* increase concurrency with per-connection locks

* only send CONNECT if the client wasnt connected already
2022-10-04 09:58:19 +01:00
Evgeny Poberezkin b686bc0c17 3.3.0 (#540) 2022-10-01 12:08:54 +01:00
Evgeny Poberezkin 29332a5e9f lints (#539)
* lints

* revert

* add import

* remove StrictData
2022-10-01 11:51:52 +01:00
Evgeny Poberezkin 9cd5f96fe4 import 2022-09-30 12:36:21 +01:00
JRoberts a2eea4f18f add deleteConnectionAsync functional api (#538) 2022-09-29 21:11:36 +04:00
Evgeny Poberezkin 413aad5139 allow passing agent store instead of path and key (#535) 2022-09-23 18:45:00 +01:00
Evgeny Poberezkin 0a9b6e4ab4 allow repeated secure with the same key (KEY command), store suspended queues in store log (#534)
* allow repeated secure with the same key (KEY command), store suspended queues in store log

* fix test
2022-09-22 13:09:39 +01:00
Evgeny Poberezkin f8505d4add make function processing sql rows module-level (#533) 2022-09-22 12:12:24 +01:00
Evgeny Poberezkin 41d3c14157 secure queue asynchronously (#532)
* secure queue asynchronously

* delete commands with internal logic errors
2022-09-20 15:11:35 +01:00
Evgeny Poberezkin d4962daf11 internal async commands (#530)
* internal async commands

* rename

* remove GADT from AgentCommand
2022-09-18 14:02:20 +01:00
Evgeny Poberezkin 085b785ee8 readme typo 2022-09-18 13:40:20 +01:00
shandEvgeny Poberezkin 84ce7a90d0 readme: add tor configuration (#524)
* readme: add tor configuration

* update docs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-09-18 13:36:02 +01:00
Evgeny Poberezkin abb3f20ed5 replace TVar with IORef in execSQL (#531) 2022-09-18 13:25:06 +01:00
Evgeny Poberezkin a3f58fdc6b api to execute any sql query (#529)
* api to execute any sql query

* agent api
2022-09-17 15:08:58 +01:00
Evgeny Poberezkin e3e05d474d wrap agent command for async command processing, to allow extension with internal commands (#528)
* wrap agent command for async command processing, to allow extension with internal commands

* change encoding for constructor
2022-09-17 14:08:49 +01:00
Evgeny Poberezkin 13a9eee0cf cancel threads asynchronously (#527) 2022-09-17 00:18:58 +01:00
Evgeny Poberezkin 959ae34f16 fix agent not stopping and race conditions (#526)
* fix agent not stopping and race conditions

* simplify
2022-09-16 13:42:13 +01:00
Evgeny Poberezkin 8c1a9c529d fix async commands (#525) 2022-09-16 08:36:11 +01:00
Evgeny Poberezkin f08d81252d Merge pull request #509 from simplex-chat/sqlcipher
switch to SQLCipher
2022-09-14 21:42:24 +01:00
Evgeny Poberezkin afecefc3ad Merge branch 'master' into sqlcipher 2022-09-14 18:22:46 +01:00
JRoberts e328ae5d06 closeAgentClient async commands clean up (#523) 2022-09-12 18:15:32 +01:00
Evgeny Poberezkin 605e6c64c6 simplify subscribeConnections (#522) 2022-09-12 13:31:41 +01:00
JRoberts 4bf9f2b535 Eq for ACommandTag (#520) 2022-09-10 22:03:51 +04:00
Evgeny Poberezkin bab6a1577b correlation IDs and command tags for async commands (#519)
* agent protocol command tags

* store/send async command correlation IDs

* fix, refactor

* delete failed command
2022-09-10 16:33:15 +01:00
Evgeny Poberezkin ca6164167e fix selecting different SMP server (#518)
* fix selecting different SMP server

* use different server with JOIN
2022-09-10 13:38:48 +01:00
Evgeny Poberezkin 42a96d6d00 refactor agent subscriptions with TMap2 (#517)
* refactor agent subscriptions with TMap2

* refactor

* refactor

* comment
2022-09-09 16:31:57 +01:00
Evgeny PoberezkinandJRoberts 6adbc56021 try async commands without servers on different servers (#516)
* refactor

* retry commands with different servers

* refactor

* remove comment

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-09-09 12:30:27 +01:00
JRobertsandEvgeny Poberezkin 882d2b123b agent async commands (#513)
* wip

* wip

* wip

* wip

* don't delete conn on join failure in async mode

* test restart

* remove unused pragma

* simplify restore

* rename variable

* refactor

* refactor

* split

* remove error

* prohibit async join via contact request

* refactor subscribeConnections

* simplify

* resum command processing on subscriptions

* unfocus tests

* schema

* remove do

* rename variable

* corrections

* move code

* refactor getPendingCommands

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-09-08 20:27:56 +04:00
Evgeny Poberezkin da5058a0c4 remove connId from message delivery queue keys (#515) 2022-09-06 14:39:50 +01:00
Evgeny Poberezkin 50c210c5c0 remove database key from SQLiteStore (#514) 2022-09-05 12:55:14 +01:00
Evgeny Poberezkin 7d53504bb3 Merge branch 'master' into sqlcipher 2022-09-05 10:39:40 +01:00
Evgeny Poberezkin e4b47825b5 functions to create and close store (#512) 2022-09-02 15:42:37 +01:00
JRoberts b215bd954d remove token if token replace fails with permanent error (#511) 2022-08-31 21:12:44 +04:00
Evgeny Poberezkin 26d149d17c interpolate sql strings (#510) 2022-08-31 17:57:38 +01:00
Evgeny Poberezkin c66a7e371f Merge branch 'master' into sqlcipher 2022-08-30 15:45:44 +01:00
Evgeny Poberezkin f2c1455a27 fix network-transport at 0.5.4 2022-08-30 15:45:15 +01:00
Evgeny Poberezkin f872c25f09 update dependencies (to not use SQL encryption unless flag is set) 2022-08-30 14:23:36 +01:00
Evgeny Poberezkin e4b77ed9e6 use SQLCipher (#507)
* use SQLCipher

* pass database key via options, use local direct-sqlcipher and sqlcipher-simple

* update stack.yaml

* use dependencies in git

* update sqlcipher dependencies
2022-08-30 12:31:41 +01:00
Evgeny Poberezkin 5894f91927 fix connections passed to ntf supervisor to include pending, not only active (#506)
* fix connections passed to ntf supervisor to include pending, not only active

* fix

* fix 2
2022-08-30 08:26:42 +01:00
Evgeny Poberezkin 1459a0613a set invitation as not accepted in case of join error (#505)
* set invitation as not accepted in case of join error

* remove unused name
2022-08-25 12:17:55 +01:00
Evgeny Poberezkin a7b39b710c fix toggleConnectionNtfs (#503) 2022-08-20 13:26:25 +01:00
Evgeny Poberezkin 212a9bc4ab enable/disable connection notifications (#501)
* enable/disable connection notifications

* filter connections when intializing/disabling subscriptions

* fix

* fix 2

* add delay

* increase test delay

* increase test delay

* fix

* increase test delays

* increase test delays
2022-08-19 15:23:57 +01:00
Evgeny Poberezkin 3fc9afbb35 move servers update before migration (so it re-tries in case of crash) 2022-08-18 08:39:22 +01:00
Evgeny Poberezkin b2d4bfd0a8 split migration to multiple transactions (trying to prevent Android crash) (#502) 2022-08-17 22:08:01 +01:00
Evgeny Poberezkin cfab5a18d3 move server update to SQL migration (#500) 2022-08-16 21:31:19 +01:00
Evgeny Poberezkin 180b4b9dcb version 3.2.0 (#499) 2022-08-16 20:45:35 +01:00
Evgeny Poberezkin 0437796232 support multiple server hosts with notifications (#498)
* support multiple server hosts with notifications

* remove unused type

* remove unused import

* update server hosts during parsing
2022-08-14 21:10:34 +01:00
Evgeny Poberezkin 02bba01c16 send host events when server hosts are connected and disconnected (#496) 2022-08-13 11:57:36 +01:00
Evgeny Poberezkin 68138c08d2 support multiple server hostnames per queue (#494)
* store SMP client version per queue

* use versioned format for queue addresses (not compatible with v1)

* multiple server hosts in queue URI/data, versioned encoding (cross-version tests fail)

* choose server host based on socksProxy setting

* test works

* multiple server addresses mostly work

* add onion hosts for provided servers

* update SMP hosts
2022-08-12 11:32:04 +01:00
Evgeny Poberezkin 6bfaa4985e expire messages after 2 days, increase max message sending interval (#490)
* expire messages after 2 days, increase max message sending interval

* rename
2022-08-11 10:15:08 +01:00
Evgeny Poberezkin 7d99c4b35c reconnect agent clients on any network config change (#489)
* reconnect agent clients on any network config change

* add Eq instances
2022-08-03 09:59:49 +01:00
Evgeny Poberezkin e9db0a1162 allow passing all network configuration to the agent (#488) 2022-08-02 13:30:00 +01:00
Evgeny Poberezkin b8c23ea8d5 3.1.3 2022-08-01 11:36:46 +01:00
Evgeny Poberezkin b76ef03dbe ntf: server stats (#487)
* nts: server stats

* ntf: collect stats, refactor

* rename property

* fixes
2022-08-01 08:42:23 +01:00
Evgeny Poberezkin fcaddb7848 agent functions to get/set network configuration (#484)
* agent functions to get/set network configuration

* fix condition
2022-07-25 13:50:46 +01:00
Evgeny Poberezkin d788c3ca95 access servers via SOCKS proxy (#482)
* access servers via SOCKS proxy

* config to pass tcp timeout and option SOCKS5 proxy to the agent
2022-07-23 13:57:10 +01:00
Evgeny Poberezkin e07121266a 3.1.2 2022-07-22 11:29:20 +01:00
Evgeny Poberezkin 2f39f055c1 Merge branch 'stable' 2022-07-22 11:28:39 +01:00
shandEvgeny Poberezkin 5d06dde757 docker: add selfhosted version (#481)
* docker: add selfhosted version

* switch to ghcup

* readme

* master -> stable

* delete git checkout

* rename docker files

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-07-22 11:26:20 +01:00
Evgeny Poberezkin eb1f9370c1 fix GET from contact connection (#480) 2022-07-21 19:42:31 +01:00
Evgeny Poberezkin d810db4eed send servers in agent events (#478)
* send servers in agent events

* remove some changes

* command/function to get connection servers

* getConnectionServers return type
2022-07-20 13:56:43 +01:00
shandEvgeny Poberezkin d8f07e8dde add hosted docker container (#479)
* add docker container

* Apply suggestions from code review

* Update README.md

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-07-20 11:21:10 +01:00
Evgeny Poberezkin 0ab90cb204 3.1.1 2022-07-19 15:08:14 +01:00
Evgeny Poberezkin 1d40bb97c2 update TLS to 1.6.0 (#462)
* update TLS to 1.6.0

This reverts commit 5b61c6bccd.

* typo
2022-07-19 10:01:47 +01:00
Evgeny Poberezkin 1ce63bee44 create notification subscriptions in batch subscribe (#477)
* create notification subscriptions in batch subscribe

* refactor

* refactor
2022-07-19 09:30:17 +01:00
Evgeny Poberezkin aafe2d43f5 clear directories on server initialization instead of removing them (to support mounted directories in docker) (#476) 2022-07-19 08:37:42 +01:00
Evgeny Poberezkin 0b259af9cb 3.1.0 2022-07-18 07:28:19 +01:00
Evgeny Poberezkin 6bbe1dfc66 use batch commands to resubscribe agent on server re-connection (#473) 2022-07-18 07:26:41 +01:00
Evgeny Poberezkin a6f401041a SMP protocol v4: batching multiple server commands/responses in a transport block (#470)
* batch server commands in one transport block

* subscribe to multiple queues using batched commands

* agent method to subscribe to multiple queues using batched commands

* refactor

* test for batched subscriptions

* delete part of connections in batched test

* add resubscribeConnections

* remove comment

* update SMP protocol doc
2022-07-17 10:10:38 +01:00
Evgeny Poberezkin 1670c9c05e 3.0.1 2022-07-15 13:25:29 +01:00
Evgeny Poberezkin cde8a11693 update stats logging and make it opt-in (#472)
* update stats logging and make it opt-in

* hSetBuffering

* update var name
2022-07-15 13:21:02 +01:00
JRoberts 3a4f8cb6eb corrections to ntf server linode script (#469) 2022-07-11 16:39:27 +04:00
Evgeny Poberezkin e75846aa38 Merge pull request #468 from simplex-chat/master
Merge master to stable
2022-07-09 12:39:51 +01:00
Evgeny Poberezkin 23496f1a34 v3.0.0 (#466)
* v3.0.0

* update changelog
2022-07-09 12:12:36 +01:00
Evgeny Poberezkin 9f5e6bfd19 ntf protocol version negotiation (#467) 2022-07-09 12:04:13 +01:00
Evgeny Poberezkin cb5ad1619c update version v3.0.0-rc.1 2022-07-08 14:48:08 +01:00
Evgeny Poberezkin 7a238812b7 ntf server: fix resubscribing to SMP server after it was restarted, test (#465) 2022-07-08 14:46:01 +01:00
Evgeny Poberezkin 991548b64d ntf server: do not resubscribe to error/ended subscriptions on restart (#464) 2022-07-06 18:20:49 +01:00
Evgeny Poberezkin cc798145d2 suspend agent without delay (#463)
* suspend agent without delay

* suspend/activate in separate atomically
2022-07-06 13:58:58 +01:00
Evgeny Poberezkin 4339218c57 v3.0.0-rc.0 2022-07-05 22:30:44 +01:00
Evgeny Poberezkin abd694bd5a parameterize protocol server by scheme (#459) 2022-07-05 21:56:14 +01:00
Evgeny Poberezkin 9c1b43791c SMP v3: encrypt message timestamp and flags together with the body between server and recipient (#457)
* SMP v3: encrypt message timestamp and flags together with the body between server and recipient

* v3 tests

* update protocol doc

* add test for max size message

* delay in notifications test

* simplify v3

* encrypt server message to the recipient when sent

* refactor

* exit on error restoring the messages

* refactor, increase test timeout

* style

* add prints to the test

* remove error from unsafeMaxLenBS

* update protocol

* lint, improve test, change func param
2022-07-05 21:08:05 +01:00
JRoberts e3d2d6fc91 fix NDELETE parsing (#461) 2022-07-04 11:26:27 +01:00
Evgeny Poberezkin 36b4179b8b update version v3.0.0-beta.4 2022-07-04 10:48:04 +01:00
Evgeny Poberezkin bc26dc1d68 save and restore server stats on restart (#460) 2022-07-04 10:45:35 +01:00
Evgeny Poberezkin 4c0164c49e fix: support msgFlag NULL in sql (#458) 2022-07-03 13:05:16 +01:00
Evgeny Poberezkin 40ae809033 v3.0.0-beta.3 / v1.0.0-beta.3 (ntf) 2022-07-02 15:08:41 +01:00
Evgeny Poberezkin 2c52c514c0 ntf: fix TRPL to send verification to the new token (#456) 2022-07-02 15:07:20 +01:00
JRobertsandEvgeny Poberezkin 57d83ae42d ntf: notify client about ntf supervisor internal errors (#455)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-07-02 12:27:09 +04:00
Evgeny Poberezkin f9cd7e5416 suspend ntf operations when agent is suspended (#453)
* suspend ntf operations when agent is suspended

* end and begin ntf operation on loop
2022-07-01 22:43:18 +01:00
Evgeny Poberezkin 77f1d45021 fix invalid ini syntax (#452) 2022-07-01 22:38:39 +01:00
JRoberts 5ff80f0688 ntf: test more scenarios of notifications mode change; test token change (#454) 2022-07-01 21:46:11 +04:00
JRoberts e622e12a7f v3.0.0-beta.2 / v1.0.0-beta.2 (ntf) 2022-07-01 15:27:32 +04:00
Evgeny Poberezkin 6b6ea78eac ntf server error handling (#448)
* ntf server error handling

* refactor

* fix
2022-07-01 12:25:08 +01:00
Evgeny Poberezkin 238a2e7fe9 handle ntf supervisor errors (#451) 2022-07-01 15:22:20 +04:00
JRobertsandEvgeny Poberezkin 82eff6c757 ntf: internal error if ntf server is not configured at SCHK on AUTH error (#450)
* ntf: internal error if ntf server is not configured at SCHK on AUTH error

* refactor

* rename

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-07-01 14:09:05 +04:00
JRoberts 0392f849aa ntf: reset subscription on SCHK AUTH error; launch only required workers (#449) 2022-07-01 13:19:56 +04:00
JRoberts 16f5fdeeb0 ntf: delete notifier creds on NSASmpDelete (#447)
* ntf: delete notifier creds on NSASmpDelete

* fix
2022-07-01 09:18:01 +01:00
JRoberts c3012173e3 ntf: recover from failed/in-deletion subscriptions (#446)
* ntf: recover from failed/in-deletion subscriptions

* check NASNew status on subscription recover

* carryOnWithDeletion

* logs

* SCHK
2022-06-30 20:18:12 +04:00
Evgeny Poberezkin f6a321e198 handle SMP errors in ntf subscriber (#441) 2022-06-30 16:04:01 +01:00
JRoberts c82fae72f2 ntf: refactor NtfSubAction (#445) 2022-06-30 15:34:16 +04:00
JRoberts 9695786aa5 ntf: separate types into their own module (#444) 2022-06-30 14:31:36 +04:00
Evgeny PoberezkinandJRoberts 56ef95e8ae ntf: fix null action ts (#443)
Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-06-30 14:01:34 +04:00
JRoberts 79d67694ed ntf: flush ntfSubQ on deleting all subscriptions (#442) 2022-06-30 13:39:24 +04:00
Evgeny Poberezkin 8dc051179a backwards compatible message log (#440) 2022-06-29 20:22:41 +04:00
JRoberts 0b25b57e3c Merge pull request #379 from simplex-chat/f/notifications
notifications
2022-06-29 16:54:51 +04:00
JRoberts 07bf2bca3b v3.0.0-beta.0 2022-06-29 16:52:36 +04:00
JRoberts 6c14372666 fix linter warning 2022-06-29 15:35:41 +04:00
JRobertsandEvgeny Poberezkin 2c7cad5378 ntf: update subscription status, error handling on SMP errors / agent events (#439)
* ntf: error handling on SMP errors / agent events

* refactor

* CASubError case

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* refactor

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* fix lint

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-29 15:14:44 +04:00
Evgeny Poberezkin c9501a23a0 remove TODOs 2022-06-29 08:40:21 +01:00
Evgeny Poberezkin 926e515bc0 fix v2 connecting via v1 contact (#438) 2022-06-29 08:38:51 +01:00
JRoberts 09b51dc44f ntf: retry to deliver notification with new push client (#437)
* ntf: retry to deliver notification with new push client

* update statuses

* remove token update

* increase tokenTTL
2022-06-28 22:03:06 +04:00
Evgeny Poberezkin 85d507d5d3 ntf-server store log (#435)
* ntf-server store log

* ntf serevr: restore log when server is started, save compacted store log

* log ntf server store changes

* test, store log works

* update ntf-server exe
2022-06-28 17:10:50 +01:00
Evgeny Poberezkin b0ac0744e2 fix server ACK for v1 SMP clients (#436)
* fix server ACK for v1 SMP clients

* add import
2022-06-28 15:36:02 +04:00
Evgeny Poberezkin ba40d75886 verifyNtfToken: change args order (#434) 2022-06-27 19:33:36 +01:00
Evgeny Poberezkin 0c7c0d066d support dev/prod APNS hosts (#433)
* support dev/prod APNS hosts

* fix test
2022-06-27 19:20:23 +01:00
JRobertsandEvgeny Poberezkin 3d6ad64d62 ntf: registerNtfToken rework, notification modes (#431)
* check mode for new subscriptions

* check token inside actions

* migration - apple -> apns

* wip

* register logic, modes

* update mode, cron config, verify token changes

* refactor

* fix test

* NTFMODE

* server: delete subscriptions on deleteToken

* refactor markNtfSubscriptionForDeletion

* remove NTFMODE

* remove subscriptions when token  is deleted

* refactor

* lint

* test

* check ntfMode

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-27 21:54:35 +04:00
Evgeny Poberezkin 51d0b48ce1 suspend/activate agent (#432)
* suspend/activate agent

* deliver pending messages before agent is suspended

* refactor
2022-06-26 14:15:33 +01:00
Evgeny Poberezkin a8260290e7 token encodings (#430) 2022-06-25 16:17:39 +01:00
JRoberts 2809963a2e ntf: register token with NotificationsMode (#429) 2022-06-25 17:34:08 +04:00
Evgeny Poberezkin d2321b750d get/set notification mode (#428)
* get/set notification mode

* update method
2022-06-25 14:48:15 +04:00
Evgeny Poberezkin c2f1dabcd0 fix agent phase, test (#427) 2022-06-25 08:13:07 +01:00
JRobertsandEvgeny Poberezkin 9e0e6b1afc ntf: server retry to deliver notification (#426)
* ntf: server retry to deliver notification

* refactor

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-24 20:40:35 +04:00
JRoberts 58622c6aaf ntf: catch worker errors (#425) 2022-06-24 20:40:25 +04:00
Evgeny Poberezkin b82452fe03 add category to mutable content notification (#424) 2022-06-24 15:35:50 +01:00
JRoberts 70e7c71762 ntf: read rcv queue separately from ntf subscription, fix deletion race condition (#423) 2022-06-24 16:51:42 +04:00
JRoberts a92827c986 ntf: notification server SDEL command; subscription deletion in agent (#421) 2022-06-23 17:05:43 +04:00
Evgeny Poberezkin f10e3f697c notification server TRPL command (#420)
* notification server TRPL command

* test

* client methods

* only remove current token registration
2022-06-23 08:35:33 +01:00
JRoberts ef4d4c9e16 ntf: remove notification subscription (#417) 2022-06-22 20:32:32 +04:00
Evgeny Poberezkin ffb4b4763c allow repeated NKEY (#419)
* allow repeated NKEY

* test
2022-06-22 14:00:46 +04:00
Evgeny Poberezkin 0d9d549cea unlock next GET with ACK (#418) 2022-06-22 08:12:18 +01:00
Evgeny Poberezkin a7c3133c35 Merge branch 'master' into f/notifications 2022-06-21 19:12:51 +01:00
Evgeny Poberezkin 5b61c6bccd revert tls to 1.5.7 (#416) 2022-06-21 18:52:26 +01:00
JRoberts d78fb8761e ntf: change default port for notifications server SMP client agent (#415) 2022-06-21 17:45:28 +04:00
JRoberts cd9cb19765 ntf: remove debug output from test 2022-06-21 14:09:05 +04:00
JRoberts 30b36cd756 ntf: notification server linode script (#413) 2022-06-21 13:59:50 +04:00
JRoberts 08a4b41691 v2.3.2 (#414) 2022-06-21 12:47:40 +04:00
Evgeny Poberezkin 756bd1eca7 remove timeout from stopping server (#412)
* remove timeout from stopping server

* s/0/infinity/
2022-06-20 14:36:59 +01:00
JRoberts 7a72488275 ntf: use separate key to encrypt NMsgMeta (#410)
* ntf: use separate key to encrypt NMsgMeta

* key negotiation

* save key on server, use for encryption

* refactor?

* store error

* NtfQueueCreds

* server - NtfCreds

* comment, rename

* fix type

* ClientNtfCreds

* encoding
2022-06-20 17:16:26 +04:00
Evgeny Poberezkin 7fa4862a83 Merge branch 'master' into f/notifications 2022-06-19 18:51:36 +01:00
Evgeny Poberezkin 0ab44b1836 get multiple messages when notification is processed (#411)
* get multiple messages when notification is processed

* change notification property
2022-06-19 13:23:35 +01:00
Evgeny Poberezkin d1db7d6f79 withStore runs in a single transaction (#408)
* remove store typeclass

* make withStore run single transaction

* combine 2 store calls

* refactor

* add exports

* fix catch

* correction
2022-06-18 18:01:04 +01:00
JRobertsandEvgeny Poberezkin ec70429106 ntf: getNotificationMessage' (#406)
* ntf: getNotificationMessage'

* refactor

* ntfTs

* fix parser

* length

* fix test

* fix encryption, test

* remove test focus

* getActiveNtfToken

* rename

* Update src/Simplex/Messaging/Protocol.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* getNtfRcvQueue

* refactor

* don't fail

* Update src/Simplex/Messaging/Notifications/Protocol.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Notifications/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Server.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* import

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-18 18:21:35 +04:00
JRoberts 31c78e516d remove FQHN validation during TLS handshake (#409) 2022-06-17 20:25:13 +04:00
Evgeny Poberezkin b58523d4a2 v2.3.1 2022-06-16 17:17:08 +01:00
Evgeny Poberezkin 9f981042b9 v2.3.1 2022-06-16 17:15:16 +01:00
JRoberts 8c2227ccce ntf: fix test (#405) 2022-06-16 19:22:21 +04:00
Evgeny Poberezkin bca44ae54a Merge branch 'master' into f/notifications 2022-06-16 15:41:30 +01:00
Evgeny Poberezkin 50ddcd3a13 log weekly and monthly active queues (#404)
* log weekly and monthly active queues

* update
2022-06-16 15:35:45 +01:00
JRobertsandEvgeny Poberezkin 3336f987c3 ntf: SCHK, cancel actions on errors, wait until action ts before executing (#402)
* SCHK

* NSACheck

* updateSubFutureTs

* dont execute too early

* fix diffTimeToMillis

* rename function

* errors

* nested status

* increase ntfSubCheckInterval

* adjust import

* Update src/Simplex/Messaging/Agent/NtfSubSupervisor.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Agent/NtfSubSupervisor.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* refactor rescheduleAction

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-16 16:11:46 +04:00
Evgeny PoberezkinandJRoberts 1d56ef3a60 agent method to GET message (#403)
* agent method to GET message

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-06-16 12:35:17 +01:00
Evgeny Poberezkin 0c45e0bf76 Merge branch 'master' into f/notifications 2022-06-16 08:49:26 +01:00
Evgeny Poberezkin 5ccebaeb21 fix transport (treat "" as eof, remove threadId), upgrade TLS to 1.6.0 (#401) 2022-06-16 08:49:07 +01:00
Evgeny Poberezkin 5f94240ed4 remove DB connection pool to prepare for using semaphores (#400) 2022-06-15 14:11:03 +01:00
JRoberts 331918bac7 ntf: agent tests (#399) 2022-06-15 14:22:55 +04:00
JRoberts f7bd8a3982 ntf: agent ntf workers implementation, store methods (#394) 2022-06-14 14:34:18 +04:00
Evgeny PoberezkinandJRoberts 68c2682e70 ios: synchronizing processing between app & nse (#398)
* ios: synchronizing processing between app & nse

* update rfc

* add sequence diagram

* manage agent phase

* track agent operation and phase changes

* update

* remove APInactive

* Update src/Simplex/Messaging/Agent/Protocol.hs

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-06-14 13:27:45 +04:00
Evgeny Poberezkin 62485b9367 re-deliver duplicate message if it was not acknowledged by the user, ignore otherwise (#397)
* re-deliver duplicate message if it was not acknowledged by the user, ignore otherwise

* delete message after ACK
2022-06-13 08:27:08 +01:00
Evgeny Poberezkin 91802fe94f Merge branch 'master' into f/notifications 2022-06-12 16:49:56 +01:00
Evgeny Poberezkin 0a71822dd0 v2.3.0: save and restore messages when the server is restarted (#395)
* save and restore messages when server is restarted

* read file line by line

* add import

* optmize restoring messages by reading the whole file

* update version to 2.3.0

* update scripts

* add script

* update readme
2022-06-12 15:59:14 +01:00
JRobertsandEvgeny Poberezkin 9c12b4edc0 ntf: agent spike, NtfSupervisor (#392)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-06-10 16:35:06 +04:00
Evgeny Poberezkin c1348aa54f SMP connection handshake v2 (#390)
* SMP connection handshake v2

* hadshake v2 [mostly] works

* all tests pass, some race conditions remain

* fix build

* fix race conditions, send CON after all HELLOs in duplexHandshake mode

* add comments

* comment

* add comments
2022-06-09 13:47:07 +01:00
Evgeny Poberezkin 4220c3bdaf Merge branch 'master' into f/notifications 2022-06-08 09:49:58 +01:00
Evgeny Poberezkin 7736ef8576 v2.2.1 2022-06-08 09:08:34 +01:00
Evgeny Poberezkin 80309a0089 fix possible leak (#391)
* fix possible leak

* remove subscriptions map from the client
2022-06-08 08:59:12 +01:00
JRoberts bfb556c860 ntf: test notification subscription (#389) 2022-06-07 19:14:51 +04:00
Evgeny Poberezkin 3f985e8fd7 Merge branch 'master' into f/notifications 2022-06-07 14:17:05 +01:00
Evgeny Poberezkin a3d1f5540d v2.2.0 2022-06-07 11:55:28 +01:00
Evgeny Poberezkin 3f69636f1a fix sockets/threads/memory leak (#388)
* fix sockets/threads/memory leak

* refactor
2022-06-07 11:52:32 +01:00
Evgeny Poberezkin 60294521f4 add msgId to ACK to avoid the risks of losing messages with concurrent delivery (in app/NSE) (#387)
* add msgId to ACK to avoid the risks of losing messages with concurrent delivery (in app/NSE)

* update ACK to only remove message and update stats if msgId matches

* add tests, fix

* rename sameMsgId/msgDeleted
2022-06-07 10:18:40 +01:00
Evgeny Poberezkin 4b3d04bd27 support message flags visible to SMP server to control notifications (and for any future extensions) (#386)
* support stopping and resuming agent  (#385)

* export agentDbPath

* support fully closing and resuming agent

* whitespace

* clean up

* support message flags visible to SMP server to control notifications (and for any future extensions)

* simplify message flags encoding

* GET command
2022-06-06 12:59:45 +01:00
JRoberts 0dc34cd287 ntf: NMSG, remove NtfTokenId from subscriptionLookup key (#384) 2022-06-06 14:52:22 +04:00
Evgeny Poberezkin 628930df1f support stopping and resuming agent (#385)
* export agentDbPath

* support fully closing and resuming agent

* whitespace

* clean up
2022-06-04 13:08:05 +01:00
JRoberts 46ba047077 ntf: IDSUB response, check notifierKey and return subId on repeat SNEW (#383) 2022-06-02 19:36:56 +04:00
JRoberts facb6f4561 ntf: separate notifierKey from SMPQueueNtf (#382) 2022-06-02 15:36:16 +04:00
JRoberts 6a27b82759 ntf: SMP subscribers logic (#381) 2022-06-02 14:52:14 +04:00
JRoberts b5ccce0f6a ntf: store get methods (#380) 2022-06-01 21:03:32 +04:00
JRoberts b60408837a notifications: SNEW draft (#378) 2022-06-01 18:42:35 +04:00
JRoberts c2ec691a46 ntf subscription diagram (#377) 2022-05-31 15:40:43 +04:00
Evgeny Poberezkin ef7c66762e update script to send test notifications 2022-05-30 21:16:25 +01:00
JRoberts 9f6316fa6d fix getDeviceNtfToken (#376) 2022-05-30 22:58:47 +04:00
JRoberts 95a2f7560d add 2.1.1 to changelog 2022-05-25 10:08:55 +04:00
Evgeny Poberezkin b67c3e5935 version 2.1.1 (#375) 2022-05-25 09:38:17 +04:00
Evgeny PoberezkinandJRoberts ce3ca08199 server: CLI warning when deleting server configuration (#371)
* server: CLI warning when deleting server configuration

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-05-24 19:45:22 +01:00
Evgeny Poberezkin fd410280b5 gracefully close sockets on client disconnection (#373)
* gracefully close sockets on client disconnection

* remove -threaded
2022-05-24 19:38:14 +01:00
Evgeny Poberezkin c74f4d729b create/verify agent schema during tests (#374)
* create/verify agent schema during tests

* add --indent to .schema, enable all tests

* remove -threaded from test
2022-05-24 19:28:40 +01:00
Evgeny Poberezkin 49070fffe0 make server compatible with old ini files (#372)
* make server compatible with old ini files

* fix

* clean up
2022-05-18 18:30:06 +01:00
JRoberts 8dda99d77f v2.1.0 (#370) 2022-05-11 15:57:29 +04:00
Evgeny PoberezkinandJRoberts 4e4eea34f9 server: configuration to expire inactive clients in ini file (#369)
* server: configuration to expire inactive clients in ini file

* corrections

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-05-11 15:43:54 +04:00
Evgeny Poberezkin 964daf5442 v2.0.0 (#360)
* v2.0.0: notification server release

* update changelog

* update changelog

* update changelog
2022-05-03 14:32:37 +01:00
Evgeny Poberezkin 6a1cea93b5 fix: disconnect socket 2022-04-30 14:09:42 +01:00
Evgeny Poberezkin ace94d7c69 log server stats (#367)
* log server stats

* separate stats updates from the existing transactions
2022-04-30 13:47:21 +01:00
Evgeny Poberezkin 1064e9c315 disconnect inactive clients (#366)
* disconnect inactive clients

* update test description

* use one thread per client to disconnect inactive clients

* remove comments

* more comments
2022-04-30 10:56:01 +01:00
Evgeny Poberezkin 2733a09a40 limit concurrency when resubscribing, method to resubscribe if not subscribed (#368) 2022-04-30 09:36:52 +01:00
Evgeny Poberezkin 7249cb0f0e close socket on connection exception (#365) 2022-04-29 13:12:30 +01:00
Evgeny Poberezkin f8ec838912 prevent servers from reconnecting when the agent is closed (#364)
* prevent servers reconnecting when the agent is closed

* fail creating NTF server connection when the agent is closed
2022-04-25 16:04:06 +01:00
Evgeny Poberezkin c6dde772b4 batch all connections in DOWN/UP agent messages (#363) 2022-04-25 08:26:52 +01:00
Evgeny Poberezkin 633cd675b5 ToJSON instance for NtfTknStatus (#362) 2022-04-22 20:02:02 +01:00
Evgeny Poberezkin 83c18272cf Merge pull request #361 from simplex-chat/ep/ntf-token-status-2
registerNtfToken returns token status
2022-04-22 18:18:45 +01:00
Evgeny Poberezkin 7652ece045 registerNtfToken returns token status 2022-04-22 17:50:30 +01:00
Evgeny Poberezkin 7774fc3271 update swift flag 2022-04-21 18:14:41 +01:00
Evgeny Poberezkin 43d24d07dd Merge pull request #334 from simplex-chat/notifications-server
push notifications server
2022-04-21 17:21:09 +01:00
Evgeny Poberezkin a2204c9b84 remove comments, add EOL 2022-04-21 17:15:41 +01:00
Evgeny Poberezkin f718a84335 change log level to error 2022-04-21 17:07:39 +01:00
Evgeny Poberezkin e6fbaf5e50 primary tokens (#356)
* primary tokens

* support repeat token registration (TODO fix tests)

* fix notifications tests

* fix/test repeat/new registrations of the same token

* re-register token when subsequent ntf command fails with AUTH error (e.g. when server is re-started)

* cancel periodic notifications when token is deleted on the server

* debug failing test on CI

* fix notification test in CI

* debug CI test

* add delay in notificaitons test after server restart
2022-04-21 17:04:26 +01:00
Evgeny Poberezkin 7541ef4c13 Merge pull request #359 from simplex-chat/master
merge master to stable
2022-04-21 15:22:45 +01:00
Evgeny Poberezkin 5eb2822d25 use cpp-options 2022-04-21 15:16:18 +01:00
Evgeny Poberezkin 0a2cad2745 version 1.1.0 (#358)
* version 1.1.0

* increase test delays to stabilize
2022-04-21 15:00:30 +01:00
Evgeny Poberezkin 4dc7d9bc77 Merge branch 'master' into notifications-server 2022-04-21 14:35:23 +01:00
JRoberts 12caa328a8 SMP confirmation timeout recovery rfc (#357) 2022-04-20 22:17:51 +01:00
Evgeny Poberezkin b5c5a8ad2a rename Subscriptions.hs/Store.hs 2022-04-20 11:54:37 +01:00
Evgeny Poberezkin 60d0f19121 Merge branch 'master' into notifications-server 2022-04-20 08:23:26 +01:00
Evgeny Poberezkin 68f52c47dd message TTL and periodic expiration; setting to prevent creation of the new queues (#355)
* message TTL and periodic expiration; setting to prevent creation of the new queues

* fix expiration interval
2022-04-20 08:14:26 +01:00
Evgeny Poberezkin 6f3e9dc603 Merge branch 'master' into notifications-server 2022-04-18 09:08:53 +01:00
Evgeny Poberezkin 17888f89a9 test notification token with agent and notifications server (#353)
* test notification token with agent and notifications server

* notification server test with APNS mock

* set environment variables in the test

* use base64url encoding in encrypted notification data
2022-04-18 08:57:55 +01:00
Evgeny Poberezkin f84439f79d function to run TCP server without TLS (#354) 2022-04-18 08:56:08 +01:00
Evgeny Poberezkin 9d8a9c4fe4 interval notifications (TCRN command) (#352)
* notifications: periodic notifications

* agent: allow repeat token registrations, delete old tokens from notification server (e.g., when database is moved to another device)

* decrypt token verification code in the agent

* check token status, send TCRN on registration if it was enabled

* fix http2/apns response handling for error responses (also, APNS seems not to send content-length header?)
2022-04-17 10:34:51 +01:00
Evgeny Poberezkin 45ddecc4b8 APNS/HTTP2 push client for notifications server (#350)
* APN push client (WIP)

* APNS push client works

* TODO comments

* comment

* send notification and process the response

* config

* e2e encryption for notification parameter

* read apns key filename and ID from env

* connect APNS client to server, fix notification JSON encoding to use dash case

* do not connect to APNS on start to pass CI tests

* fix CI test

* remove redundant import
2022-04-14 11:33:17 +01:00
Evgeny Poberezkin 327f7112d0 Merge branch 'master' into notifications-server 2022-04-13 08:52:45 +01:00
Evgeny Poberezkin d38303d5f1 fix END from disconnected clients incorrectly removing the subscriptions, remove previous PING changes that attempted to solve the problem of lost subscriptions (#351)
* Revert "increase PING timeout, add PING failure count"

This reverts commit a89e019bb0.

* Revert "PING error now throws error to restart SMPClient for more reliable re-connection (#342)"

This reverts commit 62acbc4ad4.

* only process END from the currently active client

* log ignored END

* make PING sent every 15 sec and destroying connection on failure (for testing - to be reverted)

* make removing subscription atomic too

* Revert "make PING sent every 15 sec and destroying connection on failure (for testing - to be reverted)"

This reverts commit 5520b318a2.

* refactor, aggressive PING settings for testing

* revert PING breaking connection
2022-04-12 11:35:28 +01:00
Evgeny Poberezkin 65c77c78f3 Merge branch 'master' into notifications-server 2022-04-11 10:39:41 +01:00
Evgeny Poberezkin 6382367ec0 typo 2022-04-11 10:30:34 +01:00
Evgeny Poberezkin a89e019bb0 increase PING timeout, add PING failure count 2022-04-11 10:24:02 +01:00
Evgeny Poberezkin a638486bb3 add swift flag to package.yaml 2022-04-08 15:42:22 +01:00
Evgeny Poberezkin a211e67829 Merge branch 'master' into notifications-server 2022-04-08 08:51:36 +01:00
Evgeny Poberezkin 390e99ca95 rename cabal flag 2022-04-08 08:51:07 +01:00
Evgeny Poberezkin f577fcdacf agent schema/methods/types/store methods for notifications tokens (#348)
* agent schema/methods/types/store methods for notifications tokens

* register notification token on the server

* agent commands for notification tokens

* refactor initial servers from AgentConfig

* agent store functions for notification tokens

* server STM store methods for tokens

* fix protocol client for ntfs (use generic handshake), minimal server and agent tests

* server command to verify ntf token
2022-04-08 08:47:04 +01:00
Moritz Angermann ac7ffc413e Add Flag (#349)
* Update Parsers.hs

* Update simplexmq.cabal
2022-04-07 08:44:37 +01:00
Evgeny Poberezkin fb26916eea ntf-server CLI, re-use SMP server CLI as a library (#347)
* ntf-server CLI, re-use SMP server CLI as a library

* add executable name
2022-04-03 10:37:32 +01:00
Evgeny Poberezkin 2eff717248 Merge branch 'master' into notifications-server 2022-04-02 16:19:45 +01:00
Evgeny PoberezkinandJRoberts d31958855f ntf server implementation, updated ntf protocol, ntf client based on refactored protocol client, bare-bones SMP agent to manage ntf connections (to connect to ntf server) (#338)
* process ntf server commands

* when subscription is re-created and it was ENDed, resubscribe to SMP

* SMPClientAgent draft

* SMPClientAgent: remove double tracking of subscriptions

* subscriber frame

* PING error now throws error to restart SMPClient for more reliable re-connection (#342)

* increase TCP timeout to 5 sec

* add pragmas and vacuum db (#343)

* vacuum in each connection to enable auto-vacuum (#344)

* update protocol, token verification

* refactor SMPClient to ProtocoClient, to use with notification server protocol

* notification server client, managing notification clients in the agent

* stub for push payload

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
2022-04-02 16:14:19 +01:00
JRoberts 3ba1926b1e remove manual vacuum (#346) 2022-04-01 17:16:18 +04:00
JRoberts c62730fe4c Revert "catch db connection error"
This reverts commit 1815e81c2e.
2022-04-01 16:39:32 +04:00
JRoberts bb99fdaaa2 catch db connection error (#345) 2022-04-01 09:52:13 +01:00
JRoberts a6ec93c38e vacuum in each connection to enable auto-vacuum (#344) 2022-03-31 15:26:13 +04:00
JRoberts 33f822d72c add pragmas and vacuum db (#343) 2022-03-31 14:51:59 +04:00
Evgeny Poberezkin b6e87e4a3e increase TCP timeout to 5 sec 2022-03-29 11:18:49 +01:00
Evgeny Poberezkin 62acbc4ad4 PING error now throws error to restart SMPClient for more reliable re-connection (#342) 2022-03-29 13:21:21 +04:00
Evgeny Poberezkin 4e1184d9eb Merge branch 'master' into notifications-server 2022-03-28 19:03:40 +01:00
Evgeny Poberezkin cd22e06b3a use TMap for subscription maps (#341)
* use TMap for subscription maps

* refactor

* correction
2022-03-28 18:49:17 +01:00
Evgeny Poberezkin 6ef6bedc03 refactor/optimize server queue/message store (#340)
* refactor/optimize server queue/message store

* change fst to pattern match

* server store - wrap QueueRec into TVar
2022-03-28 10:29:21 +01:00
Evgeny Poberezkin c380431b94 resubscribe concurrently when subscription is resumed (#339)
* resubscribe concurrently when subscription is resumed

* use strict modifyTVar, refactor with TMap

* add inline

* refactor
2022-03-28 07:30:29 +01:00
Evgeny Poberezkin f466fa76e5 notifications: protocol, server (#335)
* notifications: protocol

* update protocol to include subscription ID and DH public key

* update protocol, started server

* add notification server subscription DH key

* use the same command type in notifications protocol, protocol parsing, server frame

* remove empty files
2022-03-27 08:36:00 +01:00
Evgeny Poberezkin f2409e7280 Merge branch 'master' into notifications-server 2022-03-26 21:25:43 +00:00
JRoberts 337d5bdfe7 add "SMP server on Linux" section to README (#337) 2022-03-26 19:57:18 +04:00
Evgeny Poberezkin 800581b2bf core: tcp keep-alive parameters for windows (#336) 2022-03-26 13:11:29 +00:00
Evgeny Poberezkin f060914ab8 notifications server rfc/schema (#332)
* notifications server rfc/schema

* update schema/protocol
2022-03-22 16:53:01 +00:00
Evgeny Poberezkin 14d76a1582 fix keep-alive options to work on mobile (#331) 2022-03-22 08:02:43 +00:00
Evgeny Poberezkin a37b24a8c2 use TCP keep-alive instead of SMP protocol PING (#330)
* use TCP keep-alive instead of SMP protocol PING

* update header files

* use CInt

* use Int again

* use network-3.1.2.7

* use https in cabal.project

* confitional keep-alive parameters to work on mac

* pass keep-alive opts via client/agent options

* remove space
2022-03-21 16:13:34 +00:00
John Roberts 5c6ec96d64 make smp servers configurable for running agent (#326) 2022-03-10 10:49:22 +04:00
Evgeny Poberezkin 7a19ab224b make sending confirmation asynchronous (#327)
* make sending confirmation asynchronous

* confirm first queue synchronously (on JOIN, and delete connection on failure), second queue asynchronously (from REPLY msg)

* test to retry JOIN when the first attempt fails

* process confirmation delivery errors
2022-03-02 15:52:45 +00:00
Efim Poberezkin 7a611bed5a configure systemd service restart and increase file descriptors limit for Linode script (#320) 2022-02-26 18:42:16 +04:00
Efim Poberezkin a406159ed4 1.0.3 2022-02-26 18:11:07 +04:00
Efim Poberezkin 14780a47d6 prepare v1.0.3 (#324) 2022-02-26 18:07:11 +04:00
Evgeny Poberezkin 4a66f68c55 reduce server message queue quota to 128 messages (#323) 2022-02-26 17:49:21 +04:00
Evgeny Poberezkin d1e6147adf encode all nullary constructors as objects for sumTypeJSON options 2022-02-25 18:50:04 +00:00
Evgeny Poberezkin 7d1fdadef0 reduce connection handshake expiration to 2 days 2022-02-25 10:57:23 +00:00
Evgeny Poberezkin 09e2e75c42 increase queue sizes (#322)
* increase queue sizes

* remove TODO
2022-02-25 09:02:52 +00:00
Evgeny Poberezkin dff5cad1be make CPP condition depend on HOST OS (#321) 2022-02-18 08:12:08 +00:00
Evgeny Poberezkin c1f5f9d846 flip condition for JSON encoding to make android (tagged objects) default (#319) 2022-02-16 22:38:15 +00:00
Evgeny Poberezkin 9c4778b129 update condition in cabal 2022-02-16 17:28:14 +00:00
Evgeny Poberezkin 137afb68fe conditional JSON options for iOS/Android (#318)
* conditional JSON options for iOS/Android

* update cabal file
2022-02-16 08:58:35 +00:00
Efim Poberezkin 229e2607d7 remove redundant asynchronous connection code (#317) 2022-02-14 21:00:14 +04:00
Efim PoberezkinandEvgeny Poberezkin c380c79560 make new smp client attempt to reconnect on network error (#315)
* make new smp client attempt to reconnect on network error

* reconnect async wip

* remove comments

* trigger tryReconnectClient

* add subscription before trying to take smp client

* move atomically modify asyncClients

* remove logs

* corrections

* don't put error into new smp client TMVar

* UnliftIO async

* re-work

* test for connection to the server that was initially down

* fix tests

* additionally test that bob receives the message after re-connect

* make test independent of event order

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-02-10 14:48:11 +00:00
Efim Poberezkin c9994c3a2c add "yes to migrations" option (#316) 2022-02-07 11:51:39 +04:00
Evgeny Poberezkin 137ff7043d use aeson fork with the option to encode/decode nullary constructors as empty objects (#312)
* use aeson fork with the option to encode/decode nullary constructors as empty objects

* update dependencies
2022-01-29 19:36:02 +00:00
Evgeny Poberezkin 6fe3bfa980 JSON encoding of types used in simplex-chat (#311)
* JSON encoding of types used in simplex-chat

* add field names for JSON encodings, encode all error sum-types as objects (to allow extension)
2022-01-26 20:18:41 +00:00
Evgeny Poberezkin 2b857876b4 update release template 2022-01-21 18:13:02 +00:00
Evgeny Poberezkin b777a4fd93 split the server dependencies from the agent (#310)
* split the server dependencies from the agent

* remove redundant imports

* fix Main.hs
2022-01-21 17:33:47 +00:00
Efim Poberezkin e15a25d92e update Linode stackscript, amendments 2022-01-21 14:21:07 +04:00
Efim Poberezkin 1cd68f4159 update Linode stackscript (#309) 2022-01-21 12:36:42 +04:00
Efim Poberezkin 495439adf5 1.0.2 2022-01-21 11:40:49 +04:00
Efim Poberezkin 98fac579c0 switch tls from fork to original repo (#307) 2022-01-21 11:26:16 +04:00
Evgeny Poberezkin 670b3b7974 coalesce requests to connect to SMP servers, to have 1 connection per server (#305)
* coalesce requests to connect to SMP servers

* fix possible race condition when creating new SMP client

* one more race condition

* close pending SMP clients
2022-01-20 18:33:02 +00:00
Efim PoberezkinandEvgeny Poberezkin 305ae94cce move TLS handshake to a separate thread per-connection (#306)
* move handshake to a separate thread

* 1.0.2-rc04

* refactor TLS thread

* hide EOF exception

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2022-01-20 18:32:09 +00:00
Evgeny Poberezkin a9a6917056 replace file-embed with QQ (#301) 2022-01-20 18:31:09 +00:00
Efim Poberezkin 502ee39eb3 publish release as prerelease if tag contains 'rc'; update ghc version in build (#303) 2022-01-20 11:38:02 +04:00
Efim Poberezkin ac899a67c4 support TLS 1.3 (#300) 2022-01-18 18:59:02 +04:00
Efim Poberezkin 6e7089284e cease attempts to send HELLO after timeout (#298) 2022-01-18 13:50:43 +04:00
Efim Poberezkin 40efdf97de bootstrap script to download latest SMP server release for DigitalOcean droplet (#295) 2022-01-17 12:32:10 +04:00
Efim Poberezkin 7e0bcc7aa0 v1.0.1 (#294) 2022-01-15 18:39:57 +04:00
Efim Poberezkin 56fea79097 add not up to date warning for DO (#293) 2022-01-15 12:51:23 +04:00
Evgeny Poberezkin 26a01dfc40 explicitely set line buffering in stdout/stderr to log each lines when output is redirected to files (#290) 2022-01-14 08:34:34 +00:00
Efim Poberezkin 9c3962bbe3 update readme on initialization, fix servers list (#288) 2022-01-13 10:39:02 +04:00
Evgeny Poberezkin 1882e37bf0 Merge pull request #287 from simplex-chat/master
v1 release
2022-01-12 22:51:56 +00:00
Evgeny Poberezkin 45e264c398 v1 2022-01-12 16:20:55 +00:00
Evgeny Poberezkin 41047b5db8 Merge pull request #269 from simplex-chat/ep/v1-rc
v1 release
2022-01-12 16:14:07 +00:00
Evgeny Poberezkin 15f01af6cf update changelog 2022-01-12 11:13:14 +00:00
Evgeny Poberezkin bfa4911217 improve message error handling (#286)
* message envelope sizes and protocol doc corrections

* change error handling in message delivery loop
2022-01-12 10:36:22 +00:00
Efim Poberezkin d1eba1ef2f update README instructions on server fingerprint and address (#285) 2022-01-12 11:28:16 +04:00
Evgeny Poberezkin 5f20f0124a correction to E2E encrypted (double ratchet) envelope size 2022-01-11 22:08:37 +00:00
Evgeny Poberezkin 38f4cbe74f Merge branch 'master' into ep/v1-rc 2022-01-11 21:20:27 +00:00
Evgeny Poberezkin c071adb041 update changelog 2022-01-11 21:19:12 +00:00
Evgeny Poberezkin 657c9b1b37 reduce SMP envelope sizes (#284) 2022-01-11 21:01:21 +00:00
Evgeny Poberezkin 4c0414d2d6 update readme 2022-01-11 20:51:39 +00:00
Efim Poberezkin 2f342d3914 clarify wording in Linode script comment on server address 2022-01-11 21:58:40 +04:00
Efim Poberezkin 70823b899f remove Linode referral link (#283) 2022-01-11 21:11:27 +04:00
Efim Poberezkin b6aa0c886a link to smp-server by simplexchat StackScript (#282) 2022-01-11 20:57:47 +04:00
Efim Poberezkin 3909c7b10e remove Subject Alternative Names from server certificate (#281) 2022-01-11 20:48:27 +04:00
Efim Poberezkin 5306489d72 initialize server with IP and FQDN; specify FQDN as address if provided (#279) 2022-01-11 20:24:39 +04:00
Evgeny PoberezkinandEfim Poberezkin f62cc8bda5 Update CHANGELOG.md
Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2022-01-11 16:11:04 +00:00
Evgeny Poberezkin d8a61de290 Merge branch 'master' into ep/v1-rc 2022-01-11 16:02:10 +00:00
Evgeny Poberezkin 083d39be22 store missing port as empty string instead of NULL (#280) 2022-01-11 16:01:09 +00:00
Efim Poberezkin d48da40f7e add IP and FQDN to online certificate (#278) 2022-01-11 16:35:06 +04:00
Evgeny Poberezkin 1cf68ae448 note in readme 2022-01-11 07:49:49 +00:00
Evgeny Poberezkin c81e667e01 update version and changelog 2022-01-11 07:41:41 +00:00
Evgeny Poberezkin c95854ee3f Merge branch 'master' into ep/v1-rc 2022-01-11 07:28:26 +00:00
Evgeny Poberezkin b5cb5618c1 JSON instances for ConnectionRequestUri (#276) 2022-01-11 07:21:53 +00:00
Efim Poberezkin 14afa24a0a disable websockets by default (#277) 2022-01-11 11:21:02 +04:00
Efim Poberezkin 0bfac998a5 update Linode and DigitalOcean server recipes (#274) 2022-01-11 10:41:02 +04:00
Evgeny Poberezkin 0775bceb5e remove unnecessary separation of UnliftIO instance for ExceptT (#275) 2022-01-10 19:06:04 +00:00
Efim Poberezkin db5ae65f3c Merge branch 'stable' 2022-01-10 22:17:33 +04:00
Efim Poberezkin 92b3137b79 non interactive apt commands in server recipes (#272) 2022-01-10 22:08:38 +04:00
Evgeny Poberezkin 77b1d05da2 preparing v1 release 2022-01-10 14:04:41 +00:00
Evgeny Poberezkin 2a89394174 integrate double ratchet into agent (#268)
* separate skipped messages from ratchet

* return diff for skipped messages instead of the whole state (tests fail)

* fix ratchet tests

* JSON encoding/decoding for ratchet state

* schema for ratchets

* split MonadUnliftIO instance for ExceptT to a separate file

* update StrEncoding instance for Str

* ratchet store methods

* updateRatchet store method

* move E2E ratchet params to Ratchet module

* x3dh key agreement for double ratchet

* test/fix x3dh, use x3dh for ratchets initialization

* store/get x3dh keys, save ratchet of fixed X448 type

* double-ratchet encryption integration (tests fail)

* fix double ratchet

* fix padding and message length

* remove unused code for "activations"

* remove comment

* add version checks for forward/backward compatibility

* split loading ratchet and skipped message keys

* remove unused encoding instances for Algorithm types

* update ratchet initialization params
2022-01-10 12:01:54 +00:00
Efim Poberezkin 4d68042884 change initial migration date (migrations merged) (#267) 2022-01-07 17:03:00 +04:00
Efim Poberezkin 5e57c5dfd7 rename remaining occurrences of connection alias into connection id (#265) 2022-01-07 15:37:40 +04:00
Efim Poberezkin d87ff42cf5 start with internal message id 1; fix id ordering in tests (#264) 2022-01-07 14:52:37 +04:00
Evgeny Poberezkin c4f7020aae Merge pull request #263 from simplex-chat/ep/delete-control-messages
delete control messages after ACK to server
2022-01-07 08:54:20 +00:00
Evgeny Poberezkin 624360bf39 delete control messages after ACK to server 2022-01-07 08:47:35 +00:00
Evgeny Poberezkin ee5f144ea5 Merge pull request #259 from simplex-chat/ep/message-delivery
concurrent message delivery with a separate thread/queue per connection
2022-01-06 16:47:07 +00:00
Evgeny Poberezkin 7ca0ead6a2 Merge branch 'master' into ep/message-delivery 2022-01-06 16:42:16 +00:00
Evgeny Poberezkin b415537ba5 simplify message delivery 2022-01-06 16:41:39 +00:00
Evgeny Poberezkin a5a00f3d02 Merge branch 'stable' into ep/message-delivery 2022-01-06 16:38:22 +00:00
Evgeny Poberezkin dd4ccce1ba 0.5.2 2022-01-06 13:52:54 +00:00
Evgeny Poberezkin 584f230c4d update versions for 0.5.2, changelog (#261) 2022-01-06 13:50:50 +00:00
Evgeny Poberezkin 10c62e7fa2 fix message delivery when quota exceeded - making it concurrent per queue, not per server (#260)
* test: HELLO blocking message delivery on the same server

* test: quota exceeded in one queue should not block delivery in other queues

* fix test to work in stable branch

* simplify pending message delivery (#202)

* simplify pending message delivery (WIP)

* refactor

* fix concurrent message delivery

* remove type synonym
2022-01-06 08:56:19 +00:00
Evgeny Poberezkin 02c023f939 remove message statuses and unused timestamps (#257) 2022-01-06 08:41:39 +00:00
Evgeny Poberezkin 6f1d9db8ec test: quota exceeded in one queue should not block delivery in other queues 2022-01-05 21:48:40 +00:00
Evgeny Poberezkin 3c923a3dc0 test: HELLO blocking message delivery on the same server 2022-01-05 21:12:31 +00:00
Evgeny Poberezkin ec0881f50f remove message statuses and unused timestamps 2022-01-05 19:57:04 +00:00
Evgeny PoberezkinandEfim Poberezkin 488398df9f change message envelopes and encoding, unify message delivery (#252)
* types and encodings for double ratchet integration

* upgrade stack resolver

* type classes for version agreement, encode/decode connection request links and E2E params with versioning

* encode/decode client parameters (version and DH key) in SMP queue URI using query string parameters

* restore support of the current SMP queue URI format

* update AMessage to only send queues in REPLY message (not the full connection request)

* new agent message evnvelopes (tests fail)

* new message envelopes - tests pass

* store fully encrypted messages before sending

* unify message delivery via DB queue (excluding confirmation and invitation)

* remove activateSecuredQueue

* linter hints

* remove comment

* export order

* save rachet-encrypted message, not per-queue encrypted

* delete message after it is accepted by the server, reduce message delivery interval for the tests

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2022-01-05 19:52:37 +00:00
Efim Poberezkin e452c6ebff delete received messages after sending ACK to server (#256) 2022-01-05 16:52:28 +04:00
Efim Poberezkin 19edf09ae2 add deleteMsg to agent store interface (#255) 2022-01-05 16:11:52 +04:00
Efim Poberezkin 4727e568d5 fix x509 call to be compatible with openssl version 1.1.1 (#253) 2022-01-03 20:59:53 +04:00
Evgeny Poberezkin 4a73a7ecd4 remove public header from all messages, fix envelope sizes (#251)
* fix envelope sizes

* only send sender DH pub key for per-queue E2E with confirmation message
2022-01-03 14:33:30 +00:00
Evgeny PoberezkinandEfim Poberezkin 83d085cadc amend SMP protocol doc (#232)
* amend SMP protocol doc

* Update protocol/simplex-messaging.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/simplex-messaging.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/simplex-messaging.md

* Update protocol/simplex-messaging.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2022-01-02 22:48:01 +00:00
Evgeny Poberezkin f3523bbba9 make KeyHash non-optional, verify KeyHash in SMP handshake, use StrEncoding class (#250)
* make KeyHash non-optional, StrEncoding class

* change server URI format in agent config, refactor with StrEncoding

* refactor Crypto using checkAlgorithm

* refactor parsing connection requests

* prepare to validate CA fingerprint sent in client handshake

* KeyHash check in handshake

* rename type to CliCommand

* server validates keyhash sent by the client

* validate -a option when parsing

* more of StrEncoding
2022-01-02 22:24:43 +00:00
Efim Poberezkin 33bb38299b refactor server Main.hs (#248) 2022-01-02 21:49:40 +04:00
Evgeny Poberezkin f314ff1bb6 add links to overview (#249) 2022-01-01 14:22:43 +00:00
Evgeny PoberezkinandEfim Poberezkin 1737686c1e update overview (#233)
* update overview

* network/framework -> platform

* fix links

* corrections

* corrections

* correction

* correction

* corrections

* update acknowledgements

* remove old overview.md

* update cyptographic primitives, remove acknoledgement

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2022-01-01 13:59:03 +00:00
Evgeny Poberezkin 5e29e3698e binary SMP protocol encoding, split Command type to two types (#245)
* binary SMP protocol encoding (server tests fail)

* use 1 byte for bytestring length when encoding/decoding

* Encoding class, binary tags

* update server tests

* negotiate SMP version in client/server handshake

* add version columns to queues and connections

* split parsing SMP client commands and server responses to different functions

* check uniqueness of protocol tags

* split client commands and server responses/messages to separate types

* update types in SMP client

* remove pattern synonyms for SMP errors

* simplify getHandshake

* update SMP protocol encoding in protocol spec

* encode time as a number of seconds (64-bit integer) since epoch
2022-01-01 13:10:19 +00:00
Evgeny PoberezkinandEfim Poberezkin 5e3f66a4cb update DB fields presentations: port, QueueStatus, RcvMsgStatus, SndMsgStatus (#244)
* make port nullable

* make conversions of enum types to/from text explicit; remove unused statuses/fields

* Update src/Simplex/Messaging/Agent/Store.hs

* rename RcvMsgStatus constructors

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-30 09:33:47 +00:00
Evgeny Poberezkin c8178e7f1f improve crypto key type classes (#246)
* improve crypto key type classes

* add inline pragmas
2021-12-30 08:51:20 +00:00
Evgeny Poberezkin c66f9efa55 merge migrations, prepare schema for multiple queues per connection (#243)
* merge migrations into initial

* update connection/queue tables in schema, simplify creating connections

* refactor create connection

* remove queue fields from connections table

* update server constraints
2021-12-29 17:55:27 +00:00
Evgeny Poberezkin 73cad5a6c4 simple per-queue e2e encryption with NaCl crypto_box (#242)
* simple per-queue e2e encryption with NaCl crypto_box

* add e2e keys and DH secrets to schema

* agree and save shared DH secret per queue (not used yet)

* protocol changes for uniform padding and message part lengths

* correct message structure diagrams

* make per-queue E2E encryption non-optional

* refactor crypto keys

* use NaCl crypto_box for per-queue E2E encryption, remove RSA keys from queues

* remove RSA support

* merge migration with E2E DH keys

* clean up

* remove unused methods

* parsing/serializing agent messages

* remove sender timestamp from DB and code

* clean up

* slean up

* s/SMPConfMsg/SMPConfirmation/

* serializeAgentMessage = serializeClientMessage . agentToClientMsg

* simplify error handling

* update protocol docs
2021-12-29 14:27:10 +00:00
Evgeny PoberezkinandEfim Poberezkin 51a9750891 double ratchet algorithm implementation (#236)
* started double ratchet implementation

* initialize ratchets

* started ratchet encryption

* ratchet encryption

* simplify / narrow down Ratchet type

* double ratchet decryption "framework"

* advance receive ratched on skipped messages

* more ratchet decryption

* double ratchet encrypt/decrypt (TODO tests)

* double ratchet tests

* double ratchet tests

* use ratchet AD in header encryption, use header and ratchet AD as AD in message encryption

* change ratchet message error, remove Show instances

* Update tests/AgentTests/DoubleRatchetTests.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update tests/AgentTests/DoubleRatchetTests.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update tests/AgentTests/DoubleRatchetTests.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update tests/AgentTests/DoubleRatchetTests.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update tests/AgentTests/DoubleRatchetTests.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Crypto/Ratchet.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* test in the same ratchet step

* merge tests

* Update src/Simplex/Messaging/Crypto/Ratchet.hs

* Update src/Simplex/Messaging/Crypto/Ratchet.hs

* remove HMAC comment

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-25 17:13:53 +00:00
Evgeny Poberezkin 7bf97fac18 add sessionId and blockSize to the client record used internally (#241) 2021-12-24 19:31:39 +00:00
Efim Poberezkin 14954df6b9 print version on server initialization & start-up (#240) 2021-12-24 15:44:40 +04:00
Efim Poberezkin 388e4d2bcc update server recipes - Linode & DigitalOcean (#239) 2021-12-24 15:34:09 +04:00
Efim Poberezkin 0c866105d2 chain of two certificates - offline (identity) and online; switch certificates to v3 (#238) 2021-12-23 21:20:41 +04:00
Efim Poberezkin 96cf4d288c base64url encode fingerprint in agent test (#237) 2021-12-23 13:04:56 +04:00
Efim PoberezkinandEvgeny Poberezkin e2cd370513 certificate validation on client side; check stored fingerprint on server start-up; non-optional fingerprint parsing (#234, closes #155)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-12-22 23:24:58 +04:00
Evgeny Poberezkin f9f1b8f355 new overview (#235) 2021-12-21 07:46:22 +00:00
Evgeny Poberezkin f154f7ee3a overview doc updates (#216)
* overview changes

* correction

* protocol docs changes

* include reviewer comments in the doc

* encryption scheme for the recipient traffic

* TLS cipher suite
2021-12-20 12:07:31 +00:00
Evgeny Poberezkin 1873e82804 add CODEOWNERS (#231) 2021-12-20 07:08:52 +00:00
Evgeny PoberezkinandEfim Poberezkin 400e057dab use tls-unique as session ID, switch to TLS 1.2 in tls package fork (#230)
* use tls-unique as session ID, switch to TLS 1.2 in tls package fork

* Update src/Simplex/Messaging/Transport.hs

* Update src/Simplex/Messaging/Transport/WebSockets.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-19 15:10:37 +00:00
Efim PoberezkinandEvgeny Poberezkin 129246c9e6 unencrypted SMP transport over TLS; simplified handshake (#229)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-12-19 13:04:44 +04:00
Evgeny Poberezkin 8ca2d64bc9 Update FUNDING.yml 2021-12-18 16:28:06 +00:00
Evgeny Poberezkin e0acb42a28 Merge branch 'stable' 2021-12-17 15:44:58 +00:00
Evgeny Poberezkin bd2e251cae server update script 2021-12-17 13:23:37 +00:00
Evgeny Poberezkin 6b5cc3d2d5 0.5.1 2021-12-17 12:33:01 +00:00
Evgeny PoberezkinandEfim Poberezkin 5f7fe8b0dc remove client from servers subscribers map after client disconnection (#228)
Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-17 16:28:48 +04:00
Evgeny Poberezkin 1df146c702 make WebSockets use TLS Context (#227) 2021-12-16 07:30:16 +00:00
Evgeny Poberezkin 323fb1f03c remove SMP server signing responses (#226)
* remove SMP server signing responses

* keep only one session ID
2021-12-16 07:15:45 +00:00
Efim Poberezkin de01692ffd enable WebSockets over TLS (#225) 2021-12-15 22:58:47 +04:00
Efim Poberezkin bcf5e25cab create TLS ServerParams once per server run; remove tlsServerParams from agent env (fixes functional agent client for chat) (#223) 2021-12-15 19:03:34 +04:00
Evgeny Poberezkin 5aa0e97cd9 sign server responses 2021-12-15 11:52:09 +00:00
Evgeny Poberezkin cf3d0dfdc3 Transaction fields for size, session IDs, refactor (#222)
* add SMP session IDs/tls-unique to transmission

* refactor SMP transmissions: precise transmission types in server & client

* use correct session IDs

* remove TSession
2021-12-15 08:06:34 +00:00
Evgeny PoberezkinandEfim Poberezkin 83d352cfbe TLS 1.3 transport (#203)
* TLS as Transport class instance with pre-defined server certificate/key

* refactor error logging

* remove Ed25519

* refactor TLS.cGet

* TLS over TCP for Transport

* Plain -> TLS

* comment

* getLn, change supported cipher

* use non fixed certificates

* comment

* check options earlier

* wording

* headers

* Update apps/smp-server/Main.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/smp-server/Main.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* localhost -> server

* Update apps/smp-server/Main.hs

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* remove comment

* agent key and cert fixtures WIP

* certificate and key in correct order

* exitFailure

* refactor loadServerCertificate

* remove liftIO

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-15 07:48:57 +00:00
Evgeny Poberezkin 7dba734ab8 Merge pull request #221 from simplex-chat/ep/smp-sign-encrypt
SMP protocol changes: server signature keys (no signing/verification yet), recipient message bodies encryption with crypto_box
2021-12-13 12:49:41 +00:00
Evgeny PoberezkinandEfim Poberezkin a3101edbb9 Update src/Simplex/Messaging/Agent/Client.hs
Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-13 12:04:52 +00:00
Evgeny Poberezkin 95fbd70346 encrypt recipient message bodies with crypto_box 2021-12-13 10:56:26 +00:00
Evgeny Poberezkin acf5c15a05 increase SMP queue ID and message ID size to 24 bytes 2021-12-12 23:17:21 +00:00
Evgeny Poberezkin 560d9ceb71 add NaCl crypto_box scheme 2021-12-12 22:59:11 +00:00
Evgeny Poberezkin bfa05c9432 all tests pass! 2021-12-12 21:17:25 +00:00
Evgeny Poberezkin c45454d9e5 update server store log to save/restore additional keys, use Ed keys in tests instead of RSA 2021-12-12 20:51:34 +00:00
Evgeny Poberezkin f6991539a2 add server signature keys and DH keys for server->recipient msg bodis to SMP commands (server store log and agent tests are skipped) 2021-12-12 12:22:44 +00:00
Evgeny Poberezkin 5f7366b7bd Merge pull request #198 from simplex-chat/v5 2021-12-10 12:29:50 +00:00
Evgeny Poberezkin f640e71f82 remove BlockArguments extension (#220) 2021-12-10 12:21:54 +00:00
Evgeny Poberezkin 52eef2d7c7 Merge branch 'master' into v5 2021-12-10 11:52:08 +00:00
Evgeny Poberezkin f15067cf68 0.5.0 2021-12-08 15:11:27 +00:00
Evgeny PoberezkinandEfim Poberezkin ff2b975cd8 prepare v0.5.0 - update versions, changelog (#219)
* prepare v0.5.0 - update versions, changelog

* Update CHANGELOG.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-12-08 15:02:28 +00:00
Evgeny Poberezkin fe2d6607de delete contact connection with DEL command (#218) 2021-12-07 19:05:19 +00:00
Efim Poberezkin 6bdf4f3ff3 change ports for tests (fix for ports now in use on macOS) (#217) 2021-12-06 18:56:07 +04:00
Evgeny Poberezkin 218a7756f8 RJCT / rejectContact to reject contact requests (#214) 2021-12-06 13:12:10 +00:00
Evgeny Poberezkin d5310c1c6f SimpleX protocols and network overview (#215) 2021-12-06 12:28:11 +00:00
Evgeny Poberezkin 82ae5c623f Merge branch 'master' into v5 2021-12-06 09:22:45 +00:00
Evgeny Poberezkin 57c9ccfc08 use base64url encoding for public key in connection requests; only allow accepting invitations that were not accepted (#213)
* use base64url encoding for public key in connection requests; only allow accepting invitations that were not accepted

* subscribe ContactConnection, fix test to use base64url encoding in key example
2021-12-06 09:05:13 +00:00
Evgeny Poberezkin 58758e0572 Merge branch 'master' into v5 2021-12-04 21:08:38 +00:00
Evgeny Poberezkin 3e226fc3f2 add CONF/LET for invitations, use REQ/ACPT only with contact connections (#212) 2021-12-04 21:08:02 +00:00
Evgeny Poberezkin b116408ec3 Merge branch 'master' into v5 2021-12-04 18:55:23 +00:00
Evgeny Poberezkin 04c65d98da support permanent connection link ("contact") in SMP agent protocols (#143)
* open/public queue extension for SMP and SMP agent protocols

* add connection mode - invitation or contact

* use ConnectionMode with REQ and ACPT agent notification/command

* parameterize ConnectionRequest with ConnectionMode

* implement Contact connection mode for permanent connection links

* tests for contact connections
2021-12-04 18:42:08 +00:00
Evgeny Poberezkin 316dc7b320 merge protocol changes from v5 (#211) 2021-12-02 18:42:13 +00:00
Evgeny Poberezkin 6126e72d4f Merge branch 'master' into v5 2021-12-02 18:06:42 +00:00
Evgeny Poberezkin 606bbf5062 URI syntax for SMP queues and connection requests (#210)
* URI syntax for SMP queues and connection requests

* fix connection request parsing/serializing

* add ConnectionRequest sqlite instances
2021-12-02 17:43:05 +00:00
Evgeny Poberezkin bebd1e5fb9 simplex URI request 2021-11-29 23:00:26 +00:00
Evgeny PoberezkinandEfim Poberezkin db509d0311 update protocol docs (#209)
* update protocol docs

* more protocol corrections

* update connection request syntax

* Update protocol/agent-protocol.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/agent-protocol.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/agent-protocol.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/agent-protocol.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/simplex-messaging.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-11-28 21:01:34 +00:00
Evgeny Poberezkin e1002d5ac0 GADTs for cryptographic keys (#208)
* GADTs for cryptographic keys

* update tests (signature timing tests still fail)

* fix signature verification timing tests

* configurable algorithm to sign commands to SMP queues (Ed448 by default)

* add dummy Ed keys, add timing tests for Ed keys

* re-enable Connection subscriptions tests
2021-11-28 11:44:22 +00:00
Evgeny Poberezkin 99b3749890 simplify RSA private key types (#207)
* simplify RSA private key types

* remove updateSignKey method
2021-11-28 07:08:47 +00:00
Efim Poberezkin ab875198ed SMP protocol typo 2021-11-27 02:31:15 +10:00
Evgeny Poberezkin f3d79ebcda update SMP protocol to include server signature and encryption keys 2021-11-25 17:24:00 +00:00
Evgeny Poberezkin 01e8c232f0 change syntax for queue URI and connection request (#205)
* change syntax for queue URI and connection request

* separate queue URI into separate section

* correction
2021-11-22 19:08:19 +00:00
Evgeny PoberezkinandEfim Poberezkin eb7fcae31b update transport protocol to use TLS (#204)
* update transport protocol to use TLS

* typos

* s/serverKeyHash/serverIdentity/

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update protocol/simplex-messaging.md

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* corrections

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-11-22 08:52:39 +00:00
Evgeny Poberezkin eb941f7435 simplify pending message delivery (#202)
* simplify pending message delivery (WIP)

* refactor
2021-11-19 16:26:51 +00:00
Evgeny PoberezkinandEfim Poberezkin 227d83d0e7 SMP commands for notifications (NKEY/NID and NSUB/NMSG) with separate queue ID and key (#199)
* SMP commands for notifications (LSTN, NTFY) with separate queue IDs and keys

* rename Notifier types

* remove notify key and id from NEW and IDS commands (TODO add other commands)

* fix StoreLog serialization

* add commands for managing notifications

* add notification subscribers to server state, add notifier ID and key to store log

* add notifier ID and key to the queue

* refactor END notification to work for both types of subscriptions, deliver message notification (NMSG)

* process NSUB command - subscribe to message notifications

* test for message notifications

* fix SMP client function for NSUB command

* fix parse/serialize NID command

* refactor use ifM

* check duplicate notifier ID only against other notifier IDs

* refactor getQueue

* test notifier ID and key with store log

* Update src/Simplex/Messaging/Client.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* Update src/Simplex/Messaging/Server.hs

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>

* store log: s/NOTIFY/NOTIFIER/

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-11-14 18:52:29 +00:00
Evgeny Poberezkin 057d5eeb24 AES encription with auth tag after cypher text (the order in WebCrypto etc.); upgrade GHC to 8.10.7 (#197) 2021-10-24 21:03:41 +01:00
Efim Poberezkin bfcbe4bcd8 remove build matrix and create release in the end of build to avoid downtime in binary availability (#196) 2021-10-02 18:49:24 +10:00
Evgeny Poberezkin 6b5427b826 update DigitalOcean image manifest and init script to install the latest server release, add docs (#195) 2021-09-12 14:21:09 +01:00
Evgeny Poberezkin 35e6593581 0.4.1 2021-09-11 18:55:31 +01:00
Evgeny Poberezkin 33de5f6fec include migrations in the package, update docs, versions (#194)
* include migrations in the package, update versions

* update DigitalOcean version
2021-09-11 18:36:12 +01:00
Evgeny Poberezkin d1d7d51fe0 0.4.0 2021-09-11 12:03:51 +01:00
Evgeny Poberezkin 0f9226ba2a update version (0.4.0) and changelog (#193)
* update version (0.4.0) and changelog

* changelog corrections

* make tests more stable
2021-09-11 10:26:51 +01:00
Efim Poberezkin 862f25500d v4 (#192) 2021-09-09 01:30:39 +10:00
Efim Poberezkin da071a0abf Merge pull request #160 from simplex-chat/v4
v4
2021-09-09 01:19:01 +10:00
Efim Poberezkin 51491d9cd5 Merge branch 'master' into v4 2021-09-09 01:10:41 +10:00
Evgeny PoberezkinandEfim Poberezkin d973c834a5 update protocols (#190)
Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-09-08 00:25:04 +10:00
Evgeny Poberezkin e07bedac0e move tryError to Simplex.Messaging.Util (#191) 2021-09-04 19:01:54 +01:00
Evgeny Poberezkin 2ac903a2dd do not allow "," and ";" separators in smp-server hostname (#189) 2021-09-03 20:35:48 +01:00
Evgeny Poberezkin d23254f44a SMP block size via config, the client can override size set by the server (#188) 2021-09-03 20:16:10 +01:00
Evgeny Poberezkin 9a3afda290 ACK command, ackMessage, do not send ACK to SMP server on MSG (after the initial handshake) (#187) 2021-09-01 08:29:12 +01:00
Evgeny Poberezkin 68f24f90d1 export withAgentLock and do not acquire agent lock in functional API (#186) 2021-08-30 18:29:13 +01:00
Evgeny Poberezkin 75259d70cf set block size to 4096 bytes (#185) 2021-08-30 10:47:22 +01:00
Evgeny Poberezkin 96c6339306 fix error handling of sending pending messages; set block size to 8192 (#184)
* fix error handling of sending pending messages

* s/tryAction/tryError/
2021-08-29 13:10:59 +01:00
Evgeny Poberezkin 7b5ebf7bd2 change type of agent sendMessage result to AgentMsgId (#183) 2021-08-26 23:25:49 +01:00
Evgeny Poberezkin cb950ae2e4 add SMP queue quota to server config (and use TBQueue for messages) (#182) 2021-08-26 22:54:51 +01:00
Evgeny Poberezkin 26b0edabfc refactor with RecordWildCards (#181) 2021-08-22 13:19:38 +01:00
Evgeny Poberezkin cda02a3b30 support 3072 bits RSA key size (#180) 2021-08-22 10:04:49 +01:00
Evgeny Poberezkin dd5137c336 asynchronous message sending to allow accepting messages when agent (or server) is offline (#179)
* asynchronous message sending to allow accepting messages when agent (or server) is offline

* send pending messages after agent restart; test for pending messages delivery when server restarted

* test pending message delivery after agent restart

* message delivery process per server

* test layout
2021-08-12 22:15:55 +01:00
Evgeny Poberezkin e045774caa reconnect server and resubscribe connections after disconnection (#178) 2021-08-09 08:49:49 +01:00
Evgeny Poberezkin dd6a53b0d2 funding.yml (#177) 2021-08-05 19:47:22 +01:00
Evgeny Poberezkin d9084522af agent lock to avoid running subscriber and client processing in parallel; remove check that the connection is active when delivering a message (#176)
* agent lock to avoid running subscriber and client processing in parallel; remove check that the connection is active when delivering a message

* use agent lock in agent functions
2021-08-05 08:33:48 +01:00
Evgeny Poberezkin 3829479a21 Remove introductions, rename CONF/LET to REQ/ACPT (#175)
* remove intros and functions in Reader monad

* rename CONF/LET to REQ/ACPT, allowConnection to acceptConnection
2021-08-05 08:27:44 +01:00
Efim PoberezkinandEvgeny Poberezkin 8a4bced569 decrease initial delay for HELLO retries on online activation (#174)
* decrease initial delay for HELLO retries on online activation

* move retry interval to config

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-08-01 13:51:40 +01:00
Nikita Poberezkin 8ac4b77777 create cfg dir before server initialization (#173) 2021-07-25 20:29:47 +01:00
Evgeny Poberezkin 30c36b488a commit simplexmq.cabal to remove stack DEPRECATED warning (#172) 2021-07-25 20:27:09 +01:00
Evgeny Poberezkin 10fcb9771a use explicit immediate transactions in all functions that change db (#170) 2021-07-20 21:21:30 +01:00
Nikita Poberezkin 65782d7ef6 add digital ocean deployment instruction (#168)
* add digital ocean deployment instruction

* fix instruction

* fix image link and remove ssh instruction link
2021-07-12 17:22:35 +01:00
Efim Poberezkin 5e380e1d47 add tests for establishing connection asynchronously; retrieve correct key for activation on restart (#169) 2021-07-10 20:02:48 +10:00
Efim Poberezkin 9f945492dd fix markdown error in crypto rfc (#167) 2021-07-09 04:27:07 +10:00
Evgeny Poberezkin 8ba3e3e45a remove connection ID parameter from agent functions (#166)
* remove connection ID parameter from agent functions

* remove unused extension
2021-07-03 20:18:37 +01:00
Efim PoberezkinandEvgeny Poberezkin 3d9ceff691 ask client for confirmation of sender; make establishment of connection asynchronous (#163)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-07-04 04:48:24 +10:00
Evgeny Poberezkin daad3315eb upgrade stack resolver to lts-18.0 (#165)
* upgrade stack resolver to lts-18.0

* fix random package version
2021-06-30 10:35:50 +01:00
Evgeny Poberezkin e4d9b481ec refactor message meta to MsgMeta type (#164) 2021-06-30 10:29:45 +01:00
Evgeny Poberezkin 7af7272635 SQLiteStore connection pool (#162)
* SQLiteStore connection pool

* move SQLiteStore to agent env - single store instance is used for all clients
2021-06-25 18:17:11 +01:00
Evgeny Poberezkin 09c6adeabc pass migration as parameter (to use in simplex-chat) (#161)
* pass migration as parameter (to use in simplex-chat)

* add connId parameter to create/joinConnection
2021-06-24 18:39:59 +01:00
Evgeny Poberezkin d5f324cb5c SMP agent: functional API (#159)
* SMP agent: functional API (WIP)

* functional API for SMP agent, tests

* fix ICON message parameter

* use stateTVar
2021-06-13 11:11:44 +01:00
Evgeny Poberezkin bf5561c89c remove entities, remove broadcasts (#158) 2021-06-12 20:01:36 +01:00
Evgeny Poberezkin ab89963f45 introduction protocol (#156)
* commands to support introduction

* agent messages / envelopes to support introductions

* introductions and invitations table; insert record with random unique ID

* store class methods and types for introductions

* process INTRO and ACPT commands for connection introductions

* fix tests: add MonadFail constraint, remove OK response to JOIN

* process agent messages for introductions

* ICON notification when introduction is completed

* replace multiway if with case

* correction

* support random connection IDs

* save additional connection fields, refactor create connection funcs

* refactor

* refactor

* test duplex connection with random IDs

* store methods for introductions

* test introduction

* fix parsing of CON agent message

* test introduction with random connection IDs

* broadcast with random connection and broadcast IDs

* clean up sql
2021-06-11 21:33:13 +01:00
Evgeny Poberezkin 5ebb546d58 remove digitalocean deployment until ready (#157) 2021-06-06 11:20:06 +01:00
Nikita Poberezkin 46c3589604 add DigitalOcean SMP server Droplet recipe, extend readme (#150) 2021-06-01 23:10:58 +03:00
Evgeny Poberezkin dffa7a6100 groups agent protocol (#142)
* groups agent protocol

* groups to chat protocol

* update groups doc / diagram

* group protocol commands

* update group protocol

* count messages for different group operations

* broadcast and introduction as components for group protocol

* list connections in the broadcast

* protocol commands overview - using polymorphic commands

* another version of group protocol based on "broadcast" and "introduction" concepts

* update number of messages needed to establish connection

* corrections to broadcast and introduction protocols

* corrections in groups protocol

* corrections
2021-06-01 18:20:12 +01:00
Evgeny Poberezkin bc780343df broadcast commands (#154)
* broadcast commands (WIP)

* broadcasts: store and commands implementation

* test broadcast

* broadcast test

* rename migration, handle SEBadConnType errors

* query semicolons
2021-06-01 18:11:16 +01:00
Evgeny Poberezkin 84ce001598 database migrations (#153)
* database migrations

* fix: reverse order of down migrations

* use positional parameters in queries

* simplify migrations

* typo

* rename SchemaMigration to Migration

* move store initialization to IO monad
2021-05-31 16:38:35 +01:00
Evgeny Poberezkin 92bd8ef335 agent entity types (#148)
* agent entity types (WIP - fails)

* agent entities (tests pass, TODO - reduce boilerplate)

* simplify test patters

* simplify test patterns 2

* refactor testEquality for ACommand

* stricter entity parsing and correct serialization, updated tests

* fix check of entity ID

* remove unused instance Eq (Entity t)
2021-05-29 13:01:17 +01:00
Evgeny Poberezkin 85535de7d8 update script link (#152)
* update script link

* linode logo

* update logo image
2021-05-28 19:28:36 +01:00
Nikita Poberezkin 7bbc4e4109 add linode deployment recipe, modify smp-server-linode script (#151) 2021-05-28 21:00:24 +03:00
Nikita Poberezkin 248aa234d1 turn off smp server websockets support in linode script (#149) 2021-05-27 11:48:30 +03:00
Nikita Poberezkin 26a0665d81 SMP server linode StackScript (#147) 2021-05-24 22:28:10 +03:00
Nikita Poberezkin dea1845908 0.3.2 (#146) 2021-05-23 14:19:19 +03:00
Evgeny Poberezkin 8ed3eb4581 upgrade ghc to 8.10.4, stack resolver to 17.12 (#145) 2021-05-23 12:00:18 +01:00
Evgeny Poberezkin 5962c1bb3e SMP server CLI commands (#144)
SMP server CLI commands
2021-05-23 13:10:00 +03:00
Evgeny Poberezkin 5b39f51203 websockets transport (#139)
* example websockets server

* example of ws client

* type class TConnection for  generic TCP/WebSockets implementation

* support WebSockets transport

* rename TConnection methods

* revert runClient to not need transport arg

* pass the list of ports and transports via SMP server config

* remove TypeApplications

* s/Transport/TProxy/, s/TConnection/Transport/

* fix server with multiple transports, make SMP client use WS transport with port 80 (TODO fallback to WS)
2021-05-17 22:33:12 +01:00
Nikita Poberezkin 63532c8b45 simplex-messaging.md reformulation of key pairs handling part (#141) 2021-05-15 16:04:51 +01:00
Evgeny PoberezkinandEfim Poberezkin 44bec887fe 0.3.1 (#135)
Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-05-10 15:14:06 +04:00
Efim Poberezkin 8bd42035aa reorganize sqlite store test suite (#138) 2021-05-10 15:10:32 +04:00
Efim Poberezkin f7f419224f reduce transaction retry back-off factor to 1.125 (#137) 2021-05-10 14:24:08 +04:00
Evgeny Poberezkin 8ac47b9bbf reduce transaction retry back-off factor to 1.25 (31 retries) (#136) 2021-05-09 12:49:52 +01:00
Evgeny Poberezkin 305bc5cb0a update package categories (#134) 2021-05-09 11:24:40 +01:00
Efim PoberezkinandEvgeny Poberezkin fa5a8cb5cd agent: change sqlite busy retry handling; set journal_mode = WAL (#132)
* agent: change sqlite busy retry handling; set journal_mode = WAL

* fix accumulation

* add exponential back-off (*1.5) to transaction retries

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-05-09 10:24:02 +01:00
Evgeny Poberezkin 5d59e4b2bd package and module docs, remove Simplex.Markdown (moved to simplex-chat), rename Agent.Transmission to Agent.Protocol (#133)
* package and module docs, remove Simplex.Markdown (moved to simplex-chat), rename Agent.Transmission to Agent.Protocol

* move errors.md to haddock comments, Transport docs

* add CHANGELOG.md, add missing package versions

* changelog, copyright

* docs for Simplex.Messaging.Crypto

* consistent punctuation

* use absolute URLs in readme

* correction
2021-05-09 09:36:08 +01:00
Efim PoberezkinandEvgeny Poberezkin ad87442811 sqlite busy stress test (#131)
* sqlite busy stress test

* refactor withStore2

* refactor withStore2

* pragmas

* swith to IMMEDIATE sqlite transactions and add retry on ErrorBusy

* refactor

* increase timeout, print errors and results

* remove logging errors/results

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-05-08 16:30:40 +01:00
Evgeny Poberezkin 7aacee405e agent: send CON to user when the 1st party responds HELLO; fix REPLY vulnerability (#130)
* agent: send CON to user when the 1st party responds HELLO; fix REPLY vulnerability

* test for getRcvConn

* add commented OK response to JOIN command

* store: use Only newtype to select one field
2021-05-07 06:57:53 +01:00
Evgeny Poberezkin 4b9ebbbab2 move SMP server from agent commands NEW/JOIN to agent config (#129)
* move SMP server from agent commands NEW/JOIN to agent config

* fix SMPServer parser

* update agent protocol - server management

* enable agent test

* agent test with 2 servers

* create reply queue on the configured server

* choose random server

* swap bind
2021-05-06 18:53:34 +01:00
Efim Poberezkin f09c77cd31 docs: correct agent protocol abnf (#128) 2021-05-04 21:48:54 +04:00
Evgeny Poberezkin 2b0950e78e 0.3.0 2021-05-04 08:38:52 +01:00
Evgeny Poberezkin aff2d73e45 build SMP server on tags (#127)
* build SMP server on tags

* stack test -> stack build --test
2021-05-04 08:37:48 +01:00
Evgeny PoberezkinandEfim Poberezkin 377b166d8e SimplexMQ readme, remove chat client (#125)
* SimplexMQ readme, remove chat client

* link to license

* add roadmap, corrections

* corrections

* strange dot -> colon

* corrections

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-05-04 07:11:48 +01:00
Evgeny Poberezkin 1c7d7e5083 base64 encode keyhash in DB (#126) 2021-05-03 15:52:41 +01:00
Evgeny Poberezkin 772b38d154 simplify KeyHash type, store key hash in DB as binary (#122) 2021-05-03 12:38:31 +01:00
Evgeny Poberezkin b517d793d8 only allow correct key and signature sizes, improve timing mitigation (#121) 2021-05-03 12:31:23 +01:00
Evgeny Poberezkin 76e81e1cf0 fix duplex connection diagram link 2021-05-03 10:13:55 +01:00
Evgeny Poberezkin 4ec548f8b8 fix diagram links 2021-05-03 10:12:33 +01:00
Evgeny Poberezkin 885dda21ec Merge branch 'master' of simplex-chat (protocol) 2021-05-03 10:09:56 +01:00
Evgeny Poberezkin 8aabb6f0e6 move files to protocol folder 2021-05-03 10:06:16 +01:00
Evgeny Poberezkin d4378461ab package.yaml: rename simplex-messaging to simplexmq and update version (#124) 2021-05-02 21:07:45 +01:00
Efim Poberezkin 28ddef2d9c docs, smp: align with implementation (#43)
* adjust out-of-band message abnf

* define hostname and encoded using prose-val

* elaborate on base64

* corrections up to SMP procedure

* fix CONN -> NEW

* update SMP protocol to align with the implementation

* remove Possible extensions from TOC

* lists

* corrections

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-05-02 19:15:40 +01:00
Evgeny Poberezkin 7df39e9db3 update SMP version (#123) 2021-05-02 18:56:08 +01:00
Evgeny Poberezkin aac23c6481 SMP agent protocol commands semantics (#44) 2021-05-02 18:47:16 +01:00
Evgeny Poberezkin af408d50bf Merge pull request #82 from simplex-chat/v2 2021-05-02 11:34:24 +01:00
Evgeny Poberezkin 93956b65c2 Merge branch 'master' into v2 2021-05-02 11:23:41 +01:00
Efim PoberezkinandEvgeny Poberezkin b7902ee4c8 agent sqlite: store msg hashes and integrity (#118, #119, #120)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-05-02 10:48:31 +04:00
Evgeny PoberezkinandEfim Poberezkin 633b3a4bda mitigate timing attack to determine if queue exists (#117)
* mitigate timing attack to determine if queue exists

* remove timing for authenticated SEND command

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-05-01 22:07:25 +01:00
Evgeny Poberezkin 829c198e5f reserve block size (32 bit) and protocol (16 bit) in client handshake block (#116)
* reserve block size (32 bit) and protocol (16 bit) in client handshake block

* update function names

* fix abnf
2021-05-01 21:58:35 +01:00
Efim PoberezkinandEvgeny Poberezkin 6be4839703 agent: verify msg integrity based on previous msg hash and id (#110)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-05-02 00:38:32 +04:00
Evgeny Poberezkin 6ceeb2c9db save keys as binary to db, remove legacy encoding (#114)
* save keys as binary to db, remove legacy encoding

* import list
2021-04-30 09:13:18 +01:00
Evgeny Poberezkin bf6e652e41 reduce help size (#113) 2021-04-30 09:06:59 +01:00
Mark Aleksander Hil 9a8570c59c Updated ChatHelpInfo (#112) 2021-04-29 10:27:07 +01:00
Evgeny Poberezkin 729cf10ad8 test: SMP server store log (#109)
* test: SMP server store log

* test: extend store log test: queue deletion, log compacted

* test: check store log length in lines
2021-04-29 07:23:32 +01:00
Evgeny Poberezkin afc09a6ec4 Store log (#108)
* StoreLog (WIP)

* add log records to map

* revert Protocol change

* revert Server change

* fix parseLogRecord

* optionally save/restore queues to/from store log

* refactor

* refactor delQueueAndMsgs

* move store log to /var/opt/simplex

* use ini file
2021-04-26 20:34:28 +01:00
Evgeny Poberezkin 816703527a set different default server (#107)
* set different default server

* remove comment
2021-04-26 20:18:20 +01:00
Evgeny Poberezkin 5fec6c1755 Sign and verify agent messages (#106)
* sign and verify agent messages with key sent in HELLO (TODO: hardcoded block size - should use size from handshake; verify signature of HELLO message itself; possibly, different MSG status if signature was not verified (currently ignored) or failed to verify (currently fails with AGENT A_ENCRYPTION - alternatively, change it to AGENT A_SIGNATURE))

* remove hardcoded block size, make it 4096 bytes

* verify signature of HELLO message before it is added to RcvQueue

* refactor

* update doc

* rename functions
2021-04-26 20:05:46 +01:00
Evgeny Poberezkin cddff78719 binary X509 encoding for RSA key send during transport handshake (#105) 2021-04-24 12:46:57 +01:00
Efim Poberezkin 65ad2eb6a8 docs, smp, chore: remove multiline sentences line breaks; uniform lists (#42) 2021-04-19 23:14:40 +04:00
Evgeny Poberezkin 3187bc8140 chat: add connection errors in chat, fix catch (#103) 2021-04-19 08:40:23 +01:00
Efim PoberezkinandEvgeny Poberezkin 40ad6db51a return error on creation of duplicate connection (#102)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-04-19 00:46:01 +04:00
Evgeny Poberezkin 5e3bc7ee6c improve error handling (#101)
* inventory of error handling problems and types

* Change SMP protocol errors syntax

* connection errors in agent protocol (ERR CONN), STORE error -> AGENT error

* include exception in SEInternal error

* add MESSAGE errors, remove CRYPTO and SIZE errors

* agent protocol SYNTAX and AGENT errors

* BROKER errors

* group all client command (and agent response) errors

* BROKER TRANSPORT error

* simplify Client

* clean up

* transport errors

* simplify client

* parse / serialize agent errors

* differentiate crypto errors

* update errors.md

* make agent and SMP protocol errors consistent, simplify

* update doc

* test: parse / serialize protocol errors with QuickCheck

* add String to internal error

* exponential back-off when retrying to send HELLO

* refactor Client.hs

* replace fold with recursion in startTCPClient

* fail test if server did not start, refactor

* test: wait till TCP server stops

* test: refactor waiting for server to stop

* test: fail with error if server did not start/stop
2021-04-18 18:37:54 +01:00
Evgeny Poberezkin 9104ff0c4a Merge branch 'master' into v2 2021-04-18 17:03:25 +01:00
Evgeny Poberezkin 65db5cfa18 duplex procedure, update diagram (#41)
* duplex procedure, update diagram

* reduce svg whitespace

* update svg
2021-04-18 07:34:47 +01:00
Evgeny PoberezkinandEfim Poberezkin 5c1e9682c8 streams rfc draft (#64)
* streams rfc draft

* typos

* update streams RFC

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2021-04-17 08:10:23 +01:00
Evgeny PoberezkinandEfim Poberezkin 36dad0ba86 SMP agent protocol - duplex messaging (#39)
* duplex messaging commands syntax

* update duplex messaging commands

* update duplex commands/responses

* SMP messages between agents

* error for multiple skipped messages

* more syntax

* more syntax

* add diagram: creating duplex connection

* fix diagram link

* update diagram

* update duplex diagram

* add queue statuses to the diagram

* add "try sending" periods to duplex diagram

* diagram: queue status (receive/send)

* update queue status

* simplify duplex connection to only have two queues

* remove error notification sent to another agent, only notify user

* remove unused commands, add "unsubscribed" notification

* simplified commands and added connection invitation syntax

* update SMP agent protocol

* duplex protocol correction

* corrections (#40)

* SMP agent protocol

* rename duplex-messaging to agent-protocol

* minor fixes

* SMP agent protocol corrections

Co-authored-by: Efim Poberezkin <8711996+efim-poberezkin@users.noreply.github.com>
2021-04-16 19:56:53 +01:00
Evgeny Poberezkin 417066c462 change missing IDs message status syntax (#100) 2021-04-16 18:48:13 +01:00
Evgeny Poberezkin 326050651a Merge branch 'master' into v2 2021-04-14 21:30:30 +01:00
Evgeny Poberezkin 0028939155 standard X509/PKCS8 encoding for RSA keys (#98)
* key encoding primitives (WIP)

* use X509/PKCS8 to read/write server key files

* make PrivateKey type class

* clean up

* remove separate public key file

* specific import
2021-04-14 21:20:08 +01:00
Efim PoberezkinandEvgeny Poberezkin 2eb5ce24ec tests: block on tcp server creation (#99)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-04-14 02:25:57 +04:00
Evgeny Poberezkin c4475499ba remove /reset command (#96) 2021-04-12 23:56:17 +01:00
Evgeny Poberezkin 19520787a6 0.2.2 2021-04-11 18:10:31 +01:00
Evgeny Poberezkin b364f168c8 new chat UX: removed /name, add /delete and /reset, change /accept to /connect, allow command abbreviations (#95)
* remove current user name

* rename /accept to /connect, remove /chat, add /reset, allow 1-letter abbreviations

* update help

* /delete contact, separate response for confirmation

* update invatation instruction

* unset active contact only if it is the same as current
2021-04-11 18:03:55 +01:00
Evgeny Poberezkin a113067647 fix: initially blocked keys (#94) 2021-04-11 11:22:56 +01:00
Evgeny Poberezkin f6888cc9a2 markdown help (#93)
* markdown help
2021-04-11 10:17:17 +01:00
Evgeny Poberezkin 07410990a0 fix left arrow key (#92)
* fix left arrow key

* remove LambdaCase
2021-04-11 09:55:57 +01:00
Evgeny Poberezkin dbe1c4a781 Secret markdown (#91)
* secret text markdown

* refactor
2021-04-11 09:54:14 +01:00
Evgeny Poberezkin 45514c0a65 fix multiline output (messages and help) (#90) 2021-04-11 09:51:57 +01:00
Efim Poberezkin 6c7e013007 0.2.1 2021-04-10 16:49:40 +04:00
Evgeny Poberezkin a594972df0 refactor key handling (#88)
* refactor key handling

* remove constant change

* simplify

* refactor Downwards
2021-04-10 13:30:58 +01:00
Evgeny Poberezkin e78b7a66d7 markdown tests (#86) 2021-04-10 13:12:28 +01:00
Evgeny Poberezkin a2016241df support Home, End and Del keys (#87)
* add Home and End keys

* support Delete key

* simplify updateTermState
2021-04-10 12:53:22 +01:00
Evgeny Poberezkin 5c44ad0a62 Windows support in editor mode (#85)
* use System.Terminal for "editor" mode (WIP, does not work in POSIX)

* fix getKey - only return one event on control keys

* fix printing with System.Terminal

* different markdown escape for color, added black color

* fix color escapes

* make black invisible

* markdown fixes

* remove Key type, fix editor bug, refactor

* refactor: use getKey in getTermLine

* default mode is "editor", remove windows warning

* markdown: code snippet

* use ! for color markdown

* edit previous input

* clean up

* use getWindowSize from System.Terminal
2021-04-10 11:57:28 +01:00
Efim Poberezkin d0b047d197 automate changelogs (#84) 2021-04-09 18:20:09 +04:00
Evgeny Poberezkin b8241af7e9 refactor chat terminal (#83) 2021-04-08 20:20:06 +01:00
Evgeny Poberezkin 44c5a3142e Markdown (#81)
* Markdown type

* Markdown parser (WIP)

* fix markdown parser

* style markdown in messages

* one-letter color abbreviations in markdown
2021-04-08 19:32:38 +01:00
Evgeny Poberezkin ad73298936 Read server keys from files or create if absent (#79)
* move server keys to config

* add server keys from files

* create server keys if key files do not exist

* validate loaded server key pair

* refactor fromString functions

* key files in /etc/opt/simplex
2021-04-07 22:59:57 +01:00
Evgeny Poberezkin 59ef46314d Support windows terminal in basic mode (#80)
* add terminal package

* basic terminal mode with utf8 support in windows

* fix terminal input in basic mode on mac

* test code

* send StyledString to ChatTerminal

* clean up

* support StyledString with System.Terminal

* minor style change

* clean up

* minor style change
2021-04-07 20:20:32 +01:00
Evgeny Poberezkin 0fbf406800 transport encryption (#65)
* transport encryption (WIP - using fixed key, parsing/serialization works, SMP tests fail)

* transport encryption

* transport encryption: separate keys to receive and to send, counter-based IVs

* docs: update transport encryption and handshake

* transport encryption handshake (TODO: validate key hash, welcome block, move keys to system environment)

* change KeyHash type to newtype of Digest SHA256

* transport encryption: validate public key hash

* send and receive welcome block with SMP version

* refactor: parsing SMPServer

* remove unused function

* verify that client version is compatible with server version (major version is not smaller)

* update (fix) SMP server tests
2021-04-05 13:10:16 +01:00
Evgeny Poberezkin d8965d4a23 remove terminal mode "simple" (#78) 2021-04-03 21:53:16 +01:00
Evgeny Poberezkin 4353dcb3a7 enforce windows terminal mode to basic, warning message (#77) 2021-04-03 20:39:37 +01:00
Efim Poberezkin 16eb560dbc automate releases (#76) 2021-04-03 23:17:51 +04:00
Efim Poberezkin bd753dbe47 agent sqlite: enable FKs in *all* db connections (#75) 2021-03-29 19:27:05 +04:00
Efim Poberezkin 08171e987d agent sqlite: initialize database in home directory by default (#74) 2021-03-29 19:18:54 +04:00
Evgeny Poberezkin da72efc20a readme: note on docker (#73) 2021-03-09 07:17:11 +00:00
Evgeny Poberezkin 7cad237092 simplify installation instruction (#72) 2021-03-09 07:08:36 +00:00
Evgeny Poberezkin 9b0ed1d62d Windows terminal editing (#71)
* windows-compatible getChar without buffering, option to use terminal without editing

* option to choose terminal mode, conditional compilation for Windows

* conditional extension

* add basic terminal mode (no contact insertion)

* option help
2021-03-09 07:05:08 +00:00
Efim Poberezkin b9943c359d chat: subscribe to all connections on startup (#70) 2021-03-06 15:39:00 +04:00
Efim Poberezkin 11c8bee836 agent store: make newtypes for msg internal Ids (#68) 2021-03-04 23:19:12 +04:00
Efim Poberezkin 93e782b874 agent store: organize sender and broker fields into tuples (#67) 2021-03-04 23:00:00 +04:00
Efim Poberezkin cc9a67f38a agent store: explain use for various message Ids (#66) 2021-03-04 22:30:15 +04:00
Efim Poberezkin 660e35d1d1 check that sqlite library is compiled with threadsafe code (#63) 2021-03-02 22:30:59 +04:00
Evgeny Poberezkin a3990ea170 Types.hs -> Protocol.hs (#62) 2021-02-28 17:00:14 +00:00
Evgeny Poberezkin d96aeb727f improve rsa encryption (#61)
* clrify encryption schemes

* increase SMP ping delay

* include authTag and msg size in encrypted message header, pad messages to fixed size

* use newtype for Key and IV bytestrings

* rename CryptoError constructors

* refactor Word to Int conversion

* refactor padding, replace padding character

* rfc corrections, comment

* rename aesTagSize -> authTagSize

* failing test
2021-02-28 16:17:28 +00:00
Evgeny Poberezkin 927ff230da Separate db connection for each TCP client connection (#60)
* use separate db connections for each TCP client connection

* refactor atomically, increast delay in tests

* increase test delay for SMP server to start

* increase SMP ping frequency

* remove comment

* separate SQLite connection per thread, to support multi-threaded mode

* remove redundant import
2021-02-28 14:59:29 +00:00
Evgeny PoberezkinandEfim Poberezkin 7570ef9e22 SMP heartbeat to maintain the connection (#59)
* SMP heartbeat to maintain the connection

* separate SMP commands into sections

* update SMP command sections

* update SMP commands comment

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2021-02-27 15:24:05 +00:00
Efim Poberezkin 3af34dea8b agent store: accept internal ts and return internal id (#58) 2021-02-27 15:06:51 +04:00
Efim Poberezkin 135aac77ab add chat history instructions to README (#56) 2021-02-26 18:53:50 +04:00
Efim Poberezkin 8134bb0eb9 agent store: settle naming of Rcv and Snd entities (#55) 2021-02-26 18:17:56 +04:00
Efim Poberezkin a0c52033f7 add *.session.sql to .gitignore - for saving db queries (#52) 2021-02-26 18:17:56 +04:00
Efim Poberezkin dac35d8ea8 agent sqlite: merge SQLite and SQLite.Util (#54) 2021-02-26 18:17:56 +04:00
Efim Poberezkin b4ebe85ce3 agent sqlite: move sql queries closer to parameters (#53) 2021-02-26 18:17:56 +04:00
Efim Poberezkin 0f60c53a66 agent: save messages (#45) 2021-02-25 19:02:27 +04:00
Efim Poberezkin dca1bee926 chat: fix welcome line (#51) 2021-02-26 18:17:29 +04:00
Evgeny Poberezkin 1f38beb23d add mutex to prevent ansi codes appearing in the output (#50) 2021-02-26 18:17:29 +04:00
Evgeny Poberezkin 51aab978bd remove GADT syntax from existential wrappers (#49) 2021-02-26 18:17:29 +04:00
Efim Poberezkin 3a161e93ca add Dockerfile for building chat executable and instructions on running it (#48)
* [WIP] add instructions how to build project inside docker container

* docker run -> docker create

* add Dockerfile for building chat executable and instructions
2021-02-26 18:17:29 +04:00
Evgeny PoberezkinandEfim Poberezkin 1251caa4e8 terminal UI (#44)
* separate terminal IO to separate thread via queues

* terminal input arithmetics (WIP)

* editable multiline terminal input

* print messages above input area

* support Up/Down arrows

* terminal chat: move by word, move to the beginning/end of input

* insert active contact when typing starts

* refactor inserting active contact

* highlight "to contact"

* add username to prompt

* change beginning/end of line keys to shoft-arrow

* remove unused code

* add ctrl arrow key bindings

* add comment for debugging keys in terminal

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2021-02-26 18:17:29 +04:00
Efim PoberezkinandEvgeny Poberezkin 033af51d6d add instructions on how to run chat client to README (#43)
* add instructions on how to run chat client to README

* wording

* wording

* corrections to the manual

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-02-26 18:17:29 +04:00
Efim PoberezkinandEvgeny Poberezkin f3840928e0 dog-food: distinguish self from contacts (#42)
* dog-food: distinguish self from contacts

* add missing space

* rename tty functions

* return indents

* remove indentation in sent messages

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-02-26 18:17:29 +04:00
Efim Poberezkin 8d05ace601 crypto rfc: clean up e2e (#41) 2021-02-15 20:39:21 +04:00
Efim PoberezkinandEvgeny Poberezkin 1cf1c6a4b1 crypto (#33)
* add crypto rfc

* crypto rfc: fix handshake logic

* transport encryption

* e2e encryption and command verification

* crypto rfc: clarify secrecy of public keys

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-02-15 20:22:40 +04:00
Efim Poberezkin c25a6d6896 crypto: clean up magic numbers (#40) 2021-02-15 19:51:02 +04:00
Evgeny Poberezkin 435ab21e71 E2E message encryption (#38)
* RSA OAEP functions

* encrypt/decrypt using AES AEAD GSM

* fix JOIN syntax test

* encrypt/decrypt functions working

* e2e encryption!

* refactor monadic transitions

* rename liftError' to liftEitherError

* combine CryproFailable functions
2021-02-26 18:14:22 +04:00
Efim PoberezkinandEvgeny Poberezkin 469f84bb74 use cryptographic key pairs for encryption keys (#39)
* use cryptographic key pairs for encryption keys

* use speaking key types

* fix key types

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-02-26 18:14:22 +04:00
Evgeny Poberezkin 0fe41dbf16 Sign / verify SMP commands (#37)
* generate key pair

* crypto: sign/verify functions

* remove extension

* parse/serialize keys

* use RSA recipient/sender keys (TODO sign/verify)

* make PublicKey newtype, assign 0s to private_p & private_q

* replace SMP command parsing with Attoparsec

* rename types: Signed->Transmission, Transmission->SignedTransmission

* sign and verify commands (server tests skipped, agent tests pass)

* SMP client: avoid seralizing transmission twice when sending commands

* update SMP server tests to use command signatures

* remove support for "SEND :msg" syntax from SMP server protocol

* rename RSA module name to R to avoid confusion with C used for S.M.Crypto

* update key sizes to use bits `div` 8

* tidy up
2021-02-26 18:14:22 +04:00
Efim Poberezkin ce362da311 fix ghc version in build (#36) 2021-02-26 18:14:22 +04:00
Efim Poberezkin 7e9cfcce8c agent sqlite: rework schema, make methods transactional, enable foreign keys (#34)
* agent sqlite: rework schema

* agent sqlite: explicitly mark primary keys as NOT NULL

* agent sqlite: adjust connections and queues fkeys

* agent sqlite: remove ack_mode from queues tables

* [WIP] agent sqlite: refactor methods

* agent sqlite: implement transactional createRcvConn

* add comment

* agent sqlite: remove ConnAlias from createRcvConn signature

* agent sqlite: implement transactional createSndConn

* agent sqlite: remove monadic stack from store util methods

* agent sqlite: refactor getConn

* agent sqlite: rename conn -> dbConn

* agent sqlite: move transactional logic to utils

* agent sqlite: remove addServer from store interface

* fix comment

* agent sqlite: refactor getRcvQueue

* agent sqlite: refactor deleteConn

* agent sqlite: remove old deleteConn

* agent sqlite: enable FKs

* agent sqlite: refactor methods upgrading connection to duplex

* agent sqlite: uncomment not implemented methods

* agent sqlite: rename methods upgrading connection to duplex

* use liftEither

* agent sqlite: refactor update queue status methods

* agent sqlite: refactor createMsg

* clean up

* fix compilation errors in src

* fix existing tests

* clean up tests

* agent sqlite: test that foreign keys are enabled

* change private members naming

* tests: expect specific error code

* clean up

* agent sqlite: consistently separate lifts from logic to their own lines
2021-02-04 19:23:43 +04:00
Evgeny Poberezkin 1f61267308 chat prototype (#35)
* chat prototype

* chat prototype now compiles

* chat prototype works

* agent: respond SENT mId to SEND (instead of OK), ne repsonse to chat message in terminal

* chat prototype help, update commands

* chat CLI options

* add active contact to ChatClient (not used yet)

* refactor agentTransmission

* InviteContact -> AddContact

* automatically insert active contact

* highlight contact in chat

* name for invitations

* do not ask name on start

* change default server to smp.simplex.im
2021-02-26 18:13:04 +04:00
Evgeny Poberezkin 3192092349 message management (#32)
* message management rfc

* update message management rfc

* message management ideas (WIP)

* message management updated

* messages RFC

* update agent MSG constructor to include recipient/broker/sender message IDs and timestamps

* remove agent command ACK - agent automatically acknowledges server messages

* correct messages doc
2021-02-26 18:13:04 +04:00
Evgeny Poberezkin 4b5349fb94 Refactor END notifications (#31)
* kill TCP server client threads when the main server thread is killed; test END notification when server connection dies

* revert to using forkIO in the tests

* make subscriptions state change atomic

* remove commented out code

* Revert "revert to using forkIO in the tests"

This reverts commit c0bacd92e4.
2021-02-26 18:13:04 +04:00
Evgeny PoberezkinandEfim Poberezkin 17b429afe7 handle TCP disconnections (WIP) (#29)
* handle TCP disconnections (WIP)

* agent: handle SMP server disconnections

* agent: notify client about lost subscriptions when SMP server disconnects

* comments for testing functions

* remove test apps

* chore: reorder functions in Transport

* add comment

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2021-02-26 18:13:04 +04:00
Efim Poberezkin e09d3bae99 agent store: move SQLite Types -> Util (#30) 2021-01-25 20:14:33 +04:00
Evgeny Poberezkin d719b741dc suspend and delete connection (#28)
* suspend and delete connection

* agent: OFF/DEL tests, infix operators in tests

* test for subscriptions
2021-02-26 18:11:22 +04:00
Evgeny Poberezkin 19dc7b3389 subscriptions (#27)
* subscribe connection and track subscriptions

* notify client when subscription ENDs

* tcp connection timeout

* move types
2021-02-26 18:11:22 +04:00
Efim Poberezkin f50da16d0a reorganize Protocol and Agent Store (#25)
* chore: move members from Server/Transmission.hs to Protocol.hs

* chore: revert qualified SMP import for server and client

* chore: fix corrId call

* chore: move common types to Common.hs

* chore: decompose SQLite.hs

* chore: rename Agent/Transmission.hs ErrorType -> AgentErrorType

* chore: move Protocol ErrorType -> Common SMPErrorType

* chore: rename Common -> Types

* chore: revert SMPErrorType -> ErrorType
2021-02-26 18:11:22 +04:00
Evgeny Poberezkin d82c286a54 Logging (#24)
* logging with simple-logger

* refactor Agent.Client

* move logging to Agent.Client

* clean up

* log command name only
2021-02-26 18:11:22 +04:00
Evgeny Poberezkin c30a4cd1ff sql quasi-qouter (#26) 2021-02-26 18:11:22 +04:00
Evgeny Poberezkin 70fe7616f2 Sending messages end to end (#21)
* duplex connection end-to-end (working, debug logs)

* agent: send, receive, acknowledge messages

* logging proposal

* logging: client/server (dis)connections

* agent scenario testing framework

* add tests, remove logs

* clean up
2021-02-26 18:11:22 +04:00
Evgeny PoberezkinandEfim Poberezkin 65dd693c83 parse agent messages with attoparsec (#18)
* parse agent messages with attoparsec (WIP)

* agent: refactor parsers

* agent: parse commands and responses with attoparsec

* refactor UTCTime parser

* fix: updateRcvQueueStatus args

* remove outdated comment

* message parsing: PR feedback

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2021-02-26 18:11:22 +04:00
Evgeny Poberezkin d47b57ac87 correct variable name 2021-02-26 18:11:22 +04:00
Efim Poberezkin c14b0251d5 agent store: add getReceiveQueue 2021-02-26 18:11:22 +04:00
Efim Poberezkin 8ab79b066d agent store: add update queue methods (#19) 2021-02-26 18:11:22 +04:00
Evgeny Poberezkin 10fb667ff3 Receive messages (#17)
* framework to parse and process agent messages

* update SMPClient functions to accept private key

* process messages (WIP)

* agent: create reply queue and send REPLY message with qInfo

* refactor agent commands

* refactor processSMPTransmission

* agent: logic to process REPLY message
2021-02-26 18:11:22 +04:00
Efim PoberezkinandEvgeny Poberezkin c1f15c9d93 tests: randomize db file name to fix io errors (#16)
* tests: check file doesn't exist in store tests after

* Revert "tests: check file doesn't exist in store tests after"

This reverts commit de624433a9.

* [WIP] tests: randomize db file

* [WIP] tests: try runIO to get filename into after

* random db file name for agent tests

* tests: randomize DB file name

* tests: increase SMP agent start delay

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-02-26 18:11:22 +04:00
Evgeny Poberezkin 3efb15ecb3 receiving messages and remaining client functions (#15)
* SMPClient queues for messages and notifications

* style

* SMPClient: put all messages (and uncorrelated server commands) to provided TBQueue
2021-02-26 18:11:22 +04:00
Efim Poberezkin 5f59fcc969 agent store: add createMsg (#14) 2021-01-15 19:30:11 +04:00
Evgeny Poberezkin 72a1082822 SMPClient returns errors via ExceptT (#13) 2021-02-26 18:09:26 +04:00
Evgeny Poberezkin 2e6ba85308 SMP client library (#9)
* functions to send SMP commands and receive responses

* refactor agent: use SMPClient

* fix tests, remove ServerClient.hs

* refactor processCommand

* fix Agent.hs

* fix SMPClient, tests

* "forever" to SMPClient process
2021-02-26 18:09:26 +04:00
Efim Poberezkin b02bd42f84 ci: cache dependencies (#11) 2021-02-26 18:09:26 +04:00
Evgeny Poberezkin 02a15f9f25 agent tests (#8) 2021-02-26 18:09:26 +04:00
Efim Poberezkin 1c5325babf agent store: add positive updateQueueStatus tests for snd and rcv conns (#10) 2021-01-12 19:01:53 +04:00
Evgeny Poberezkin 241e3a29a8 Merge pull request #1 from simplex-chat/client
SMP agent implementation
2021-01-12 08:45:38 +00:00
Evgeny Poberezkin 7522d8b24f readme: link to releases 2021-01-11 19:25:37 +00:00
Evgeny Poberezkin df16ce1dfc rename workflow, build/version badges 2021-01-11 19:23:36 +00:00
Evgeny Poberezkin 26007818ff readme: released version link 2021-01-11 19:14:54 +00:00
Evgeny Poberezkin 253f4e39c9 JOIN command (#4)
* parse JOIN command

* parse JOIN command

* serialize NEW command

* parse and serialize CON response

* process JOIN command: SEND confirmation (WIP)

* process response to the confirmation from JOIN command

* remove comment
2021-01-11 18:31:10 +00:00
Efim PoberezkinandEvgeny Poberezkin 75570350a8 GitHub workflow for tests (#7)
* ci: add github workflow for tests

* ci: break test

* ci: fix folder name

* ci: fix test

* ci: break test

* fix test

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
2021-01-11 22:21:35 +04:00
Efim Poberezkin f91e0c7387 Merge pull request #6 from simplex-chat/update-queue-status-tests
agent store: add updateQueueStatus tests
2021-01-11 20:33:20 +04:00
Efim Poberezkin 123fe0e3e2 agent store: adjust tests hierarchy 2021-01-11 20:28:20 +04:00
Efim Poberezkin 7f22dcd496 agent store: add updateQueueStatus tests 2021-01-11 19:34:30 +04:00
Evgeny Poberezkin 6f8b3787b0 server: refactor syntax tests to only allow one command (#5)
* server: refactor syntax tests to only allow one command

* simplify smpServerTest
2021-01-11 14:04:21 +00:00
Efim Poberezkin f5aaab3861 agent store: fix updateQueueStatus 2021-01-10 23:43:57 +04:00
Efim Poberezkin b9cdc6a09d agent store: add updateQueueStatus; TODO finalize, tests 2021-01-10 16:08:19 +04:00
Evgeny Poberezkin ef028c135d agent SQLiteStore: method stubs 2021-01-09 20:54:41 +00:00
Evgeny Poberezkin 394803b3cf remove/fix compiler warnings 2021-01-09 20:48:07 +00:00
Evgeny Poberezkin ea6496ca24 better MonadUnliftIO instance for ExceptT; fix tests 2021-01-09 20:35:19 +00:00
Evgeny Poberezkin d1c5e9df7b agent: ErrorType constructor for store errors, exceptions are caught 2021-01-09 19:55:42 +00:00
Evgeny Poberezkin 3dbdb948ae agent: respond with INV (TODO: store exception blows up; select does not work without port 2021-01-09 19:23:32 +00:00
Evgeny Poberezkin a1c4ce6448 refactor store: simplify 2021-01-09 13:04:13 +00:00
Evgeny Poberezkin 0d0a12f778 refactor store: m (Either e a) => ExceptT e m a 2021-01-09 11:18:52 +00:00
Evgeny Poberezkin 75f58f8ba4 remove unnecessary bindings 2021-01-09 08:31:21 +00:00
Efim Poberezkin af5bd101cb agent store: make create connection methods return () 2021-01-08 15:51:33 +04:00
Efim Poberezkin 19238c6cc4 agent store: add deleteConn and tests 2021-01-07 17:58:46 +04:00
Efim Poberezkin 6dff023965 test: set up and tear down db per test 2021-01-07 14:51:49 +04:00
Efim Poberezkin e692cb0119 add addRcvQueue and tests 2021-01-07 14:00:53 +04:00
Evgeny Poberezkin 51cc602bdb agent store: refactor addSndQueue 2021-01-06 20:22:00 +00:00
Efim Poberezkin 5a6b1c67a9 add test testAddSndQueue 2021-01-06 23:01:53 +04:00
Efim Poberezkin 94d0be5d27 add test for addSndQueue 2021-01-06 22:35:04 +04:00
Efim Poberezkin 61092e8371 add implemented addSndQueue 2021-01-06 22:11:55 +04:00
Efim Poberezkin 1c32fccb39 [WIP] add addSndQueue 2021-01-06 18:03:55 +04:00
Evgeny Poberezkin 6e6ad0fb9b agent store: getConn with tests 2021-01-05 20:31:01 +00:00
Evgeny Poberezkin a555c4e510 agent store: getSndQueue 2021-01-05 19:42:50 +00:00
Evgeny Poberezkin 4a19602be6 agent store: getRcvQueue 2021-01-05 19:34:47 +00:00
Efim Poberezkin a28462d496 add select functions drafts 2021-01-05 19:44:44 +04:00
Efim Poberezkin 799d179f6f add createSndConn test 2021-01-05 18:03:57 +04:00
Evgeny Poberezkin 5d2e77f41d test: agent SQLite createRcvConnection 2021-01-04 20:25:02 +00:00
Evgeny Poberezkin f8e25189db move all tests from main Test to ServerTests 2021-01-04 18:49:28 +00:00
Efim Poberezkin 2e761b20f6 remove commented queries 2021-01-04 18:01:11 +04:00
Efim Poberezkin 9a1ffb349a add insertSndQueue, insertSndConnection and implementation of createSndConn 2021-01-04 17:48:29 +04:00
Evgeny Poberezkin 15cf2e9fff upsertServer: use select to get server ID 2021-01-04 10:39:20 +00:00
Evgeny Poberezkin 650971fa02 method to insert connection (#3) 2021-01-03 18:05:50 +00:00
Evgeny Poberezkin 3d4f43a051 add NOT NULL constraints 2021-01-03 12:47:12 +00:00
Evgeny Poberezkin 6526e421e8 insertWithLock 2021-01-03 11:44:07 +00:00
Evgeny Poberezkin 48967167c4 add table mutexes for insert queries (to get inserted ID thread-safely via single connection) 2021-01-03 11:31:06 +00:00
Evgeny Poberezkin d260a464d6 add error handling, function to process SMP responses 2021-01-03 10:42:41 +00:00
Evgeny Poberezkin 24bca7650e throw exception from Transport.StartTCPClient when no provided address works (and on empty list of addresses) 2021-01-02 16:55:43 +00:00
Evgeny Poberezkin 42ab7fb6ac test against localhost 2020-12-30 21:26:46 +00:00
Evgeny Poberezkin 80b4ff365d TCP client: try all resolved addresses, not just the first 2020-12-30 21:25:49 +00:00
Evgeny Poberezkin f3cd3eac58 add protocol version in TCP welcome 2020-12-30 18:50:50 +00:00
Efim Poberezkin 3c1834f93f implement addServer command 2020-12-30 18:54:17 +04:00
Evgeny Poberezkin 44b58a5bf0 fix: agent communicates with local SMP server 2020-12-29 21:21:51 +00:00
Evgeny Poberezkin cdf8695bef send NEW command to SMP server (response is not received for some reason...) 2020-12-29 20:51:09 +00:00
Efim Poberezkin d6e1aeacdf write null sender queue on initial connection insert 2020-12-29 22:19:20 +04:00
Efim Poberezkin 92d170f178 add queries drafts for insertion of server, queue, connection 2020-12-29 21:30:00 +04:00
Evgeny Poberezkin fdf35ba214 stub function to process client commands 2020-12-29 13:32:45 +00:00
Evgeny Poberezkin 9d15035fb3 client/agent transmission types, parse NEW/INV commands 2020-12-29 13:11:46 +00:00
Evgeny Poberezkin dabef7f0d1 use quasiqoutes for queries 2020-12-28 21:23:28 +00:00
Evgeny Poberezkin cf38b4dddf agent: Transmission type 2020-12-28 20:42:47 +00:00
Efim Poberezkin c47f583574 add unique constraints todo 2020-12-28 23:50:04 +04:00
Efim Poberezkin dea5c6478a change fields to blobs 2020-12-28 23:32:05 +04:00
Efim Poberezkin 205c6bf117 add servers table to database 2020-12-28 22:06:01 +04:00
Evgeny Poberezkin 87f2c20ee4 type CorrelationId -> newtype CorrId (to avoid incorrect order in tuple) 2020-12-28 17:41:30 +00:00
Evgeny Poberezkin a61d97aee0 rename Agent/Command -> Agent/Transmission 2020-12-28 17:08:48 +00:00
Evgeny Poberezkin 43baacc5be move SMP server specific methods from Transport to Server/Transmission 2020-12-28 17:07:11 +00:00
Evgeny Poberezkin d95936a770 Merge pull request #2 from simplex-chat/correlation-id
add corellationId to SMP protocol server (WIP)
2020-12-28 16:56:03 +00:00
Evgeny Poberezkin f61ad27fcd docs: add correlation IDs to examples 2020-12-28 16:28:57 +00:00
Evgeny Poberezkin 7f1d3da202 refactor msgResp helper function 2020-12-28 16:24:58 +00:00
Evgeny Poberezkin e7581a91a8 test: update tests to include correclation ID in the tests themselves 2020-12-28 15:39:28 +00:00
Evgeny Poberezkin 4b8f6417f8 add correlationId to SMP transmissions, fix tests 2020-12-28 13:55:53 +00:00
Evgeny Poberezkin 02cb6068a2 create all tables 2020-12-27 21:32:30 +00:00
Evgeny Poberezkin 9d8df33cf7 delete agent database from source 2020-12-27 21:28:30 +00:00
Efim Poberezkin a6bd03bf99 add agent ddl draft 2020-12-27 22:06:37 +04:00
Evgeny Poberezkin e418498334 move queries to code 2020-12-27 12:48:46 +00:00
Evgeny Poberezkin 18ab9650b4 move queries to code 2020-12-27 12:47:39 +00:00
Evgeny Poberezkin 7bcd5ebcef agent: creating schema 2020-12-27 10:26:08 +00:00
Evgeny Poberezkin a3b53f68c5 empty database schema 2020-12-26 21:22:17 +00:00
Evgeny Poberezkin 501e8a140a rename Env.hs -> Env/SQLite.hs 2020-12-26 13:01:57 +00:00
Evgeny Poberezkin 027b91098d MonadAgentStore class 2020-12-26 12:55:23 +00:00
Evgeny Poberezkin f7e0b26297 rename Agent/ConnStore -> Agent/Store 2020-12-26 11:06:21 +00:00
Evgeny Poberezkin c195c79367 SMP agent: data types for connections and message delivery 2020-12-26 10:58:50 +00:00
Evgeny Poberezkin 2a87a1b5c9 diagram: invalid syntax response without roundtrip 2020-12-25 22:07:30 +00:00
Evgeny Poberezkin d4c4c467f4 diagram: agent replies invalid commands without roundtrip 2020-12-25 21:59:36 +00:00
Evgeny Poberezkin 14d7619bbf comments to commands flow 2020-12-25 21:44:02 +00:00
Evgeny Poberezkin c5a8bf6f40 SMP agent: receive/send via TCP and queues 2020-12-25 21:36:51 +00:00
Evgeny Poberezkin 2362a22f7a diagram: 1 commands queue per SMP queue to correlate commands with responses 2020-12-25 20:54:20 +00:00
Evgeny Poberezkin b234c0afee started SMP agent 2020-12-25 16:46:18 +00:00
Evgeny Poberezkin 2ed24e683e rename Config to ServerConfig 2020-12-25 16:46:17 +00:00
Efim Poberezkin 6249bf749e fix SMP command mentioned in Duplex subscribe 2020-12-25 19:18:37 +04:00
Efim Poberezkin 990ff1d7ed describe subscribe command 2020-12-25 19:05:07 +04:00
Efim Poberezkin c35b838e06 remove list from commands 2020-12-25 18:16:20 +04:00
Efim Poberezkin 77a532ff13 mark agent commands description as outdated 2020-12-25 18:14:44 +04:00
Evgeny Poberezkin 3eb33363a0 update diagram 2020-12-25 13:34:45 +00:00
Evgeny Poberezkin c53cbac8d2 diagram: move command corellation to the client group 2020-12-25 13:30:48 +00:00
Evgeny Poberezkin f20593c422 diagram: remove access to persistence from server thread 2020-12-25 10:51:02 +00:00
Evgeny Poberezkin 456eb0634f design comparison 2020-12-25 10:10:51 +00:00
Evgeny Poberezkin e2a4f09684 alternative agent design 2020-12-25 10:02:14 +00:00
Evgeny Poberezkin fe66b35192 added answers to commands flow questions 2020-12-24 20:55:30 +00:00
Evgeny Poberezkin 42aec97cd3 types for agent commands, responses and messages sent to another agent 2020-12-24 20:43:23 +00:00
Efim Poberezkin 10b109e384 prepare QA sections for agent commands 2020-12-24 22:16:04 +04:00
Efim Poberezkin e3ea6aa245 add description and flow of SMP agent commands, WIP 2020-12-24 21:57:08 +04:00
Evgeny Poberezkin 968f3b605a update agent diagram, remove client library diagram 2020-12-23 21:55:54 +00:00
Evgeny Poberezkin 716a8a2067 diagrams for client 2020-12-22 20:33:34 +00:00
Evgeny Poberezkin 3cb2421373 move modules to folder Simplex.Messaging.Server 2020-11-22 18:22:20 +00:00
Evgeny Poberezkin 28a3db5edc update protocol to use term "queue" to mean "SMP connection", CONN -> NEW 2020-10-22 11:29:48 +01:00
Evgeny Poberezkin efe799f460 LF -> CRLF 2020-10-18 12:50:14 +01:00
Evgeny Poberezkin f8aa11f419 corrections 2020-10-17 21:58:03 +01:00
Evgeny Poberezkin adc8af16b3 add subscription END notification, corrections 2020-10-17 21:09:49 +01:00
Evgeny Poberezkin fc2e51053a change command names and errors 2020-10-15 15:47:47 +01:00
Evgeny Poberezkin dc223f564d update syntax 2020-10-10 22:16:36 +01:00
Evgeny Poberezkin ea77a4653f unify and simplify SMP (#38)
* unify and simplify SMP

* use cameCase in ABNFs

* update diagrams

* update ABNF RFC

* update protocol syntax

* table of contents
2020-10-10 21:47:17 +01:00
Evgeny Poberezkin b80fd37a0f Remove connection ID and alternative flow (#22)
* docs: remove connection ID from simplex-messaging.md, #8

* docs: remove alternative flow in simplex-messaging, #20

* docs: update simplex diagrams to remove connection ID

* docs: remove connection ID from graph-chat

* docs: update duplex connection diagram to remove connection ID, closes #8
2020-01-26 21:34:14 +00:00
Efim Poberezkin 9d9afb1150 Rename edge-messaging to simplex messaging (#15)
* rename edge-messaging protocol related files

* rename edge-messaging protocol to simplex messaging protocol

* adjust wordings for simplex connections
2020-01-25 13:19:34 +04:00
Evgeny Poberezkin 12e9fbf75a Connection URIs (#11)
* simplex connection: why RU and SU should be different, closes #5

* remove api allowing connection URIs change
2020-01-22 19:09:07 +04:00
Evgeny PoberezkinandEfim Poberezkin 43c2c9d02d Split to edge-messaging and graph-chat (#1)
* docs: graph-messaging protocol [WIP]

* docs: creating and using graph-messaging connection

* docs: subtitle

* docs: [WIP] graph-chat protocol

* docs: graph-chat establishing duplex connection

* apply minor typo fixes and wording adjustments to protocols docs

* rename file graph-messaging -> edge-messaging

* update test graph-messaging -> edge-messaging

* correction re CID

* duplex connection correction

* rename folder graph-messaging -> edge-messaging

* add duplex connection

* update edge-messaging to match graph-chat

* update symbols in graph-chat

* sequence diagram: creating duplex connection

* fix indentation

* edge-messaging: REST API, crypto, IDs, URIs

* REST API endpoints summary

* Rest -> REST

* REST API additional requirements

* adjust wordings and fix typos (#2)

* update readme (#4)

* update readme

* send message story and diagram

* edge-messaging: alternative flow of creating connection

* remove old diagrams

* apply minor fixes

* correct readme

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>

* graph-chat: added duplex connection types

* graph-chat: comment on user profile visibility

* clarify duplex diagram

Co-authored-by: Efim Poberezkin <efim.poberezkin@gmail.com>
2020-01-21 21:01:48 +00:00
Evgeny Poberezkin bea5f0525a docs: simplex connection 2019-12-23 23:18:17 +00:00
Evgeny Poberezkin 50074eb33b docs: update message diagram to account for user-server auth and encryption 2019-12-21 22:34:58 +00:00
Evgeny Poberezkin dfa80d6756 docs: update connection diagram to include user-server authentication and encryption 2019-12-21 22:20:56 +00:00
Evgeny Poberezkin 1b81010e4a docs: update connection diagram 2019-12-21 15:13:03 +00:00
Evgeny Poberezkin 91470bc65d docs: adding connection diagram 2019-12-21 15:04:51 +00:00
Evgeny Poberezkin b9f4c50771 docs: update sending message diagram 2019-12-21 14:31:42 +00:00
Evgeny Poberezkin d1022dc7fc docs: update sending message diagram 2019-12-21 14:21:01 +00:00
Evgeny Poberezkin b469a90116 docs: update sending message diagram 2019-12-21 14:11:51 +00:00
Evgeny Poberezkin 90d6857128 docs: sending message diagram 2019-12-21 14:08:57 +00:00
587 changed files with 140296 additions and 1413 deletions
+4
View File
@@ -0,0 +1,4 @@
* @epoberezkin @spaced4ndy
/Dockerfile @shumvgolove
/scripts/docker/ @shumvgolove
/scripts/main/ @shumvgolove
+2
View File
@@ -0,0 +1,2 @@
github: simplex-chat
open_collective: simplex-chat
+44
View File
@@ -0,0 +1,44 @@
name: 'Set Swap Space'
description: 'Add moar swap'
branding:
icon: 'crop'
color: 'orange'
inputs:
swap-size-gb:
description: 'Swap space to create, in Gigabytes.'
required: false
default: '10'
runs:
using: "composite"
steps:
- name: Swap space report before modification
shell: bash
run: |
echo "Memory and swap:"
free -h
echo
swapon --show
echo
- name: Set Swap
shell: bash
run: |
export SWAP_FILE=$(swapon --show=NAME | tail -n 1)
echo "Swap file: $SWAP_FILE"
if [ -z "$SWAP_FILE" ]; then
SWAP_FILE=/opt/swapfile
else
sudo swapoff $SWAP_FILE
sudo rm $SWAP_FILE
fi
sudo fallocate -l ${{ inputs.swap-size-gb }}G $SWAP_FILE
sudo chmod 600 $SWAP_FILE
sudo mkswap $SWAP_FILE
sudo swapon $SWAP_FILE
- name: Swap space report after modification
shell: bash
run: |
echo "Memory and swap:"
free -h
echo
swapon --show
echo
+4
View File
@@ -0,0 +1,4 @@
{
"template": "${{UNCATEGORIZED}}",
"pr_template": "- ${{TITLE}}"
}
+303
View File
@@ -0,0 +1,303 @@
name: build
on:
push:
branches:
- master
- stable
tags:
- "v*"
pull_request:
jobs:
# =============================
# Create release
# =============================
# Create release, but only if it's triggered by tag push.
# On pull requests/commits push, this job will always complete.
maybe-release:
runs-on: ubuntu-latest
steps:
- name: Clone project
if: startsWith(github.ref, 'refs/tags/v')
uses: actions/checkout@v3
with:
submodules: recursive
- name: Build changelog
id: build_changelog
if: startsWith(github.ref, 'refs/tags/v')
uses: simplex-chat/release-changelog-builder-action@v5
with:
configuration: .github/changelog_conf.json
failOnError: true
ignorePreReleases: true
commitMode: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Create release
if: startsWith(github.ref, 'refs/tags/v')
uses: simplex-chat/action-gh-release@v2
with:
body: |
See full changelog [here](https://github.com/simplex-chat/simplexmq/blob/master/CHANGELOG.md).
Commits:
${{ steps.build_changelog.outputs.changelog }}
prerelease: true
files: |
LICENSE
fail_on_unmatched_files: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# =============================
# Main build job
# =============================
build:
name: "ubuntu-${{ matrix.os }}-${{ matrix.arch }}, GHC: ${{ matrix.ghc }}"
needs: maybe-release
env:
apps: "smp-server xftp-server ntf-server xftp"
runs-on: ${{ matrix.runner }}
services:
postgres:
image: postgres:15
env:
POSTGRES_HOST_AUTH_METHOD: trust # Allows passwordless access
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
# Maps tcp port 5432 on service container to the host
- 5432:5432
strategy:
fail-fast: false
matrix:
include:
- os: 22.04
os_underscore: 22_04
arch: x86-64
runner: "ubuntu-22.04"
ghc: "8.10.7"
should_run: ${{ !(github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }}
- os: 22.04
os_underscore: 22_04
arch: x86-64
runner: "ubuntu-22.04"
ghc: "9.6.3"
should_run: true
- os: 24.04
os_underscore: 24_04
arch: x86-64
runner: "ubuntu-24.04"
ghc: "9.6.3"
should_run: true
- os: 22.04
os_underscore: 22_04
arch: aarch64
runner: "ubuntu-22.04-arm"
ghc: "9.6.3"
should_run: true
- os: 24.04
os_underscore: 24_04
arch: aarch64
runner: "ubuntu-24.04-arm"
ghc: "9.6.3"
should_run: true
steps:
- name: Clone project
if: matrix.should_run == true
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Docker Buildx
if: matrix.should_run == true
uses: simplex-chat/docker-setup-buildx-action@v3
- name: Setup swap
if: matrix.ghc == '8.10.7' && matrix.should_run == true
uses: ./.github/actions/swap
with:
swap-size-gb: 20
- name: Install PostgreSQL 15 client tools
if: matrix.os == '22.04' && matrix.should_run == true
shell: bash
run: |
# Import the repository signing key
sudo install -d /usr/share/postgresql-common/pgdg
sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc
# Add the PostgreSQL APT repository
sudo sh -c 'echo "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
# Update repository and install postgresql tools
sudo apt update
sudo apt -y install postgresql-client-15
- name: Build and cache Docker image
if: matrix.should_run == true
uses: simplex-chat/docker-build-push-action@v6
with:
context: .
load: true
file: Dockerfile.build
tags: build/${{ matrix.os }}:latest
build-args: |
TAG=${{ matrix.os }}
GHC=${{ matrix.ghc }}
- name: Cache dependencies
if: matrix.should_run == true
uses: actions/cache@v4
with:
path: |
~/.cabal/store
dist-newstyle
key: ubuntu-${{ matrix.os }}-${{ matrix.arch }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplexmq.cabal') }}
- name: Start container
if: matrix.should_run == true
shell: bash
run: |
docker run -t -d \
--device /dev/fuse \
--cap-add SYS_ADMIN \
--security-opt apparmor:unconfined \
--name builder \
-v ~/.cabal:/root/.cabal \
-v /home/runner/work/_temp:/home/runner/work/_temp \
-v ${{ github.workspace }}:/project \
build/${{ matrix.os }}:latest
- name: Build smp-server, xftp-server (postgresql) and tests
if: matrix.should_run == true
shell: docker exec -t builder sh -eu {0}
run: |
chmod -fR 777 ~/.cabal ./dist-newstyle || :; git config --global --add safe.directory '*'
cabal clean
cabal update
cabal build --jobs=$(nproc) --enable-tests -fserver_postgres
mkdir -p /out
for i in smp-server xftp-server simplexmq-test; do
bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
chmod +x "$bin"
mv "$bin" /out/
done
strip /out/smp-server /out/xftp-server
- name: Copy simplexmq-test from container
if: matrix.should_run == true
shell: bash
run: |
docker cp builder:/out/simplexmq-test .
- name: Copy smp-server, xftp-server (postgresql) from container and prepare it
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
id: prepare-postgres
shell: bash
run: |
printf 'bins<<EOF\n' > bins.output
printf 'hashes<<EOF\n' > hashes.output
for i in smp-server xftp-server; do
name="${i}-postgres-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"
docker cp builder:/out/$i $name
path="${{ github.workspace }}/$name"
hash="SHA2-256($name)= $(openssl sha256 $path | cut -d' ' -f 2)"
printf '%s\n' "$path" >> bins.output
printf '%s\n\n' "$hash" >> hashes.output
done
printf 'EOF\n' >> bins.output
printf 'EOF\n' >> hashes.output
cat bins.output >> "$GITHUB_OUTPUT"
cat hashes.output >> "$GITHUB_OUTPUT"
- name: Build everything else (standard)
if: matrix.should_run == true
shell: docker exec -t builder sh -eu {0}
run: |
cabal build --jobs=$(nproc)
mkdir -p /out
for i in ${{ env.apps }}; do
bin=$(find /project/dist-newstyle -name "$i" -type f -executable)
strip "$bin"
chmod +x "$bin"
mv "$bin" /out/
done
- name: Copy binaries from container and prepare them
id: prepare-regular
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
shell: bash
run: |
docker cp builder:/out .
printf 'bins<<EOF\n' > bins.output
printf 'hashes<<EOF\n' > hashes.output
for i in ${{ env.apps }}; do
name="$i-ubuntu-${{ matrix.os_underscore }}-${{ matrix.arch }}"
mv ./out/$i ./$name
path="${{ github.workspace }}/$name"
hash="SHA2-256($name)= $(openssl sha256 $path | cut -d' ' -f 2)"
printf '%s\n' "$path" >> bins.output
printf '%s\n\n' "$hash" >> hashes.output
done
printf 'EOF\n' >> bins.output
printf 'EOF\n' >> hashes.output
cat bins.output >> "$GITHUB_OUTPUT"
cat hashes.output >> "$GITHUB_OUTPUT"
- name: Upload binaries
if: startsWith(github.ref, 'refs/tags/v') && matrix.should_run == true
uses: simplex-chat/action-gh-release@v2
with:
append_body: true
prerelease: true
fail_on_unmatched_files: true
body: |
${{ steps.prepare-regular.outputs.hashes }}
${{ steps.prepare-postgres.outputs.hashes }}
files: |
${{ steps.prepare-regular.outputs.bins }}
${{ steps.prepare-postgres.outputs.bins }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Test
if: matrix.should_run == true && matrix.arch == 'x86-64'
timeout-minutes: 120
shell: bash
env:
PGHOST: localhost
run: |
i=1
attempts=1
${{ (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/tags/v')) }} && attempts=3
while [ "$i" -le "$attempts" ]; do
if ./simplexmq-test; then
break
else
echo "Attempt $i failed, retrying..."
i=$((i + 1))
sleep 1
fi
done
if [ "$i" -gt "$attempts" ]; then
echo "All "$attempts" attempts failed."
exit 1
fi
+60
View File
@@ -0,0 +1,60 @@
name: Build and push Docker image to Docker Hub
on:
push:
tags:
- "v*"
jobs:
build-and-push:
name: Build and push Docker image
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- app: smp-server
app_port: "443 5223"
- app: xftp-server
app_port: 443
steps:
- name: Clone project
uses: actions/checkout@v4
with:
submodules: recursive
- name: Log in to Docker Hub
uses: simplex-chat/docker-login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Extract metadata for Docker image
id: meta
uses: simplex-chat/docker-metadata-action@v5
with:
images: ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.app }}
flavor: |
latest=auto
tags: |
type=semver,pattern=v{{version}}
type=semver,pattern=v{{major}}.{{minor}}
type=semver,pattern=v{{major}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: simplex-chat/docker-build-push-action@v6
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
build-args: |
APP=${{ matrix.app }}
APP_PORT=${{ matrix.app_port }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
+47
View File
@@ -0,0 +1,47 @@
name: Reproduce latest release
on:
workflow_dispatch:
schedule:
- cron: '0 2 * * *' # every day at 02:00 night
jobs:
reproduce:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Get latest release
shell: bash
run: |
curl --proto '=https' \
--tlsv1.2 \
-sSf -L \
'https://api.github.com/repos/simplex-chat/simplexmq/releases/latest' \
2>/dev/null | \
grep -i "tag_name" | \
awk -F \" '{print "TAG="$4}' >> $GITHUB_ENV
- name: Execute reproduce script
run: |
${GITHUB_WORKSPACE}/scripts/simplexmq-reproduce-builds.sh "$TAG" || :
- name: Check if build has been reproduced
env:
url: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_URL }}
user: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_USER }}
pass: ${{ secrets.STATUS_SIMPLEX_WEBHOOK_PASS }}
run: |
if [ -f "${GITHUB_WORKSPACE}/${TAG}-simplexmq/_sha256sums" ]; then
exit 0
else
curl --proto '=https' --tlsv1.2 -sSf \
-u "${user}:${pass}" \
-H 'Content-Type: application/json' \
-d '{"title": "👾 GitHub: Runner", "description": "⛔️ '"$TAG"' did not reproduce."}' \
"$url"
exit 1
fi
+13 -1
View File
@@ -1,2 +1,14 @@
*.lock
*.cabal
*.db
*.db.bak
*.session.sql
tests/tmp
dist-newstyle/
cabal.project.local
cabal.project.local~
.hpc/
*.tix
.coverage
+6
View File
@@ -0,0 +1,6 @@
[submodule "cbits/libbbs"]
path = cbits/libbbs
url = https://github.com/simplex-chat/libbbs.git
[submodule "cbits/blst"]
path = cbits/blst
url = https://github.com/supranational/blst.git
+1
View File
@@ -0,0 +1 @@
- ignore: {name: "Use underscore"}
@@ -0,0 +1,53 @@
# XFTPClientAgent Pattern
## TOC
1. Executive Summary
2. Changes: client.ts
3. Changes: agent.ts
4. Changes: test/browser.test.ts
5. Verification
## Executive Summary
Add `XFTPClientAgent` — a per-server connection pool matching the Haskell pattern. The agent caches `XFTPClient` instances by server URL. All orchestration functions (`uploadFile`, `downloadFile`, `deleteFile`) take `agent` as first parameter and use `getXFTPServerClient(agent, server)` instead of calling `connectXFTP` directly. Connections stay open on success; the caller creates and closes the agent.
`connectXFTP` and `closeXFTP` stay exported (used by `XFTPWebTests.hs` Haskell tests). The `browserClients` hack, per-function `connections: Map`, and `getOrConnect` are deleted.
## Changes: client.ts
**Add** after types section: `XFTPClientAgent` interface, `newXFTPAgent`, `getXFTPServerClient`, `closeXFTPServerClient`, `closeXFTPAgent`.
**Delete**: `browserClients` Map and all `isNode` browser-cache checks in `connectXFTP` and `closeXFTP`.
**Revert `closeXFTP`** to unconditional `c.transport.close()` (browser transport.close() is already a no-op).
`connectXFTP` stays exported (backward compat) but becomes a raw low-level function — no caching.
## Changes: agent.ts
**Imports**: replace `connectXFTP`/`closeXFTP` with `getXFTPServerClient`/`closeXFTPAgent` etc.
**Re-export** from agent.ts: `newXFTPAgent`, `closeXFTPAgent`, `XFTPClientAgent`.
**`uploadFile`**: add `agent: XFTPClientAgent` as first param. Replace `connectXFTP``getXFTPServerClient`. Remove `finally { closeXFTP }`. Pass `agent` to `uploadRedirectDescription`.
**`uploadRedirectDescription`**: change from `(client, server, innerFd)` to `(agent, server, innerFd)`. Get client via `getXFTPServerClient`.
**`downloadFile`**: add `agent` param. Delete local `connections: Map`. Replace `getOrConnect``getXFTPServerClient`. Remove finally cleanup. Pass `agent` to `downloadWithRedirect`.
**`downloadWithRedirect`**: add `agent` param. Same replacements. Remove try/catch cleanup. Recursive call passes `agent`.
**`deleteFile`**: add `agent` param. Same pattern.
**Delete**: `getOrConnect` function entirely.
## Changes: test/browser.test.ts
Create agent before operations, pass to upload/download, close in finally.
## Verification
1. `npx vitest --run` — browser round-trip test passes
2. No remaining `browserClients`, `getOrConnect`, or per-function `connections: Map` locals
3. `connectXFTP` and `closeXFTP` still exported (XFTPWebTests.hs compat)
4. All orchestration functions take `agent` as first param
+953
View File
@@ -0,0 +1,953 @@
# 6.5.1
Version 6.5.1.0
XFTP client:
- backwards compatible file header decoding.
# 6.5.0
Version 6.5.0.17
SMP agent:
- improve subscriptions
- reduce memory usage and retries during initial subscription (#1758)
- fix race resulting in pending subscriptions never subscribed (#1756)
- batch processing of subscription results and errors (#1652)
- reduce memory usage of active subscriptions.
- drop message after N reception attempts (#1762)
- fix possible deadlocks of queue overloading when processing messages (#1713)
- improved APIs for short link management and creation.
- support multiple link owners in link data (#1701)
SMP server:
- store messages in PostgreSQL (#1622).
- reduce memory usage with PostgreSQL database - do not use queue cache (#1637)
- fix in-memory server not restoring queue/service associations after 2+ restarts (#1618)
XFTP server:
- support PostgreSQL database.
- add server page.
- support uploads from web clients.
Servers:
- better socket leak prevention during TLS handshake, NetworkError type to bette diagnose connection errors (#1619)
- use "=" as default INI key-value separator (#1767)
# 6.4.4
Servers:
- fix server pages when source code is not specified.
- include commit SHA in printed version and in web page (#1608).
SMP server:
- support short SimpleX addresses in server information page (#1600).
- wrap all queries in transactions (#1603).
SMP agent:
- chat relay address type for short links (#1602).
- extend xrcp certificate validity 1 hour in the past, to allow out of sync clocks (#1601).
# 6.4.3
SMP agent:
- fix some connection errors by updating contact request server hosts to match server in short link (#1597).
SMP server:
- support short link URI as queue identifier in control port commands (#1596).
# 6.4.2
SMP server:
- fix memory leak when connection interrupts straight after client connects.
- do not include repeated queue blocking into stats/quota.
XFTP server:
- prometheus metrics
# 6.4.1
SMP protocol:
- create notification credentials via NEW command that creates the queue (#1586)
SMP server:
- control port session improvements (#1591)
- additional stat counter for ntf credentials created together with the queue (#1589)
# 6.4.0
SMP protocol (server/client):
- support associated queue data and short connection links (see [RFC](./rfcs/2025-03-16-smp-queues.md)).
- service certificates to optimize subscriptions.
SMP agent:
- support retries for interactive connection handshakes.
- use web port 443 by default for preset servers.
- use static RNG function to avoid creating dynamic C stubs when generating sntrup keys (it was detected as Dynamic Code Loading in GrapheneOS).
- different timeouts for interactive and background operations.
Ntf server:
- PostgreSQL storage.
- Prometheus metrics.
- use service certificates.
- fix repeat token registration.
# 6.3.2
Servers:
- enable store log by default (#1501).
SMP server:
- reduce memory usage (#1498)
SMP agent:
- handle client/agent version downgrades after connection was established (#1508).
# 6.3.1
Servers:
- handle ECONNABORTED error on client connections.
- reproducible builds.
- blocking records for content moderation.
- update script (simplex-servers-update) downloads scripts from the specified or the latest stable tag.
SMP server:
- support for PostgreSQL database for queue records for higher traffic servers.
- fix old clients sending messages to new servers (#1443)
- remove empty journals when opening message queues and expiring idle queues (#1456, #1458).
- additional start options (#1465):
- `maintenance` to run all start/stop operations without starting server.
- `skip-warnings` to ignore the last corrupted line in store log (can happen on abnormal termination).
Ntf server:
- record date of last token activity, to allow expiring inactive tokens.
- additional token invalidation reasons in logs.
SMP agent:
- store message sent to multiple connections only once, to reduce storage when sending to groups (#1453).
- encrypt messages on delivery, to reduce database writes (#1446).
- don't block method calls on congested sockets for better concurrency (#1454).
- check notification token status on client connection.
- option to skip SQLite vacuum on migrations.
# 6.3.0
SMP agent: fix joining connection after failure by using the same ratchet.
# 6.2.2
SMP server:
- add optional Prometheus metrics (#1411).
Build:
- remove three modules from client library.
# 6.2.0
Version 6.2.0.7
Build:
- client_library flag to build only used modules in the clients, remove package yaml
SMP server:
- journal storage for messages (BETA).
- prevent race condition when deleting queue and to avoid "orphan" messages (#1395).
SMP agent:
- support SMP and XFTP server roles (storage/proxy) and operators (#1343).
- treat blocked STM and other critical errors that offer restart as temporary for message delivery (#1405).
- fix inconsistent state after app restart while accepting contact request (#1412).
# 6.1.3
SMP server: fix restoring notification credentials.
# 6.1.2
Servers: more reliable restoring of state.
SMP server: reduced memory usage and faster start.
Notifications: compensate for iOS notifications being dropped by Apple while device is offline (#1378):
- Ntf server: send multiple SMP notifications in one iOS notification.
- Agent: get multiple messages for one iOS notification.
# 6.1.1
SMP:
- stop server faster (#1371)
- add STORE error (#1372)
# 6.1.0
Version 6.1.0.7
SMP server and client:
- transport block encryption (#1317).
Agent:
- batch and optimize iOS notifications processing (#1308, #1311, #1313, #1316, #1330, #1331, #1333, #1337, #1346).
- allow receiving multiple messages from single iOS notification (#1355, #1362).
- prepare connection to accept to avoid race condition with events (#1365).
- transport isolation mode "Session" (default) to use new SOCKS credentials when client restarts or SOCKS proxy configuration changes (#1321).
Ntf server:
- control port (#1354).
- enable pings on ntf subscriptions, to resubscribe on reconnection (#1353).
SMP server:
- support multiple server ports (#1319).
- support serving HTTPS and SMP transport on the same port (#1326, #1327).
- persist iOS notifications to avoid losing them when Ntf server is offline (#1336, #1339, #1350).
- fix lost notification subscriptions (#1347).
- reject SKEY with different key earlier, at verification step (#1366).
- pass server information via CLI during server initialization (#1356).
- show version on server page (#1341).
- explicit graceful shutdown on SIGINT (#1360).
XRCP (remote access protocol):
- use SHA3-256 in hybrid key agreement (#1302).
- session encryption with forward secrecy (#1328).
# 6.0.5
SMP agent:
- support generic SOCKS proxy (without isolate-by-auth).
- reduce max message sizes
# 6.0.4
SMP server:
- better performance/memory: fewer map updates on re-subscriptions (#1297), split and reduce STM transactions (#1294)
- send DELD when subscribed queue is deleted (#1312)
- add created/updated/used date to queues to manage expiration (#1306)
XFTP server: truncate file creation time to 1 hour (#1310)
Servers:
- bind control port only to 127.0.0.1 for better security in case of firewall misconfiguration (#1280)
- reduce memory used for period stats (#1298)
Agent: process last notification from list (#1307)
- report receive file error with redirected file ID, when redirect is present (#1304)
- special error when deleted user record is not in database (#1303)
- fix race when sending a message to the deleted connection (#1296)
- support for multiple messages in a single notification
Ntf server:
- only use SOCKS proxy for servers without public address (#1314)
# 6.0.3
Agent:
- fix possible stuck queue rotation (#1290).
SMP server:
- batch END responses when subscribed client switches to reduce server and client traffic.
- reduce STM transactions for better performance.
- add stats for END events and for SUB/DEL event batches.
- remove "expensive" stats to save memory.
# 6.0.2
SMP agent:
- fix stuck connection commands when a server is not responding.
- store query errors, reduce slow query threshold to 1ms.
Notification server:
- reduce PING interval to 1 minute.
- fix subscriptions disabled on race condition (only mark subscriptions with END status when received via the active connection).
# 6.0.1
SMP agent:
- support changing user of the new connection.
- do not start delivery workers when there are no messages to deliver.
- enable notifications for all connections.
- combine database transactions when subscribing.
SMP server:
- safe compacting of store log.
- fix possible race when creating client that might lead to memory leak.
Dependencies: upgrade tls to 1.9
# 6.0.0
Version 6.0.0.8
Agent:
- enabled fast handshake support.
- batch-send multiple messages in each connection.
- resume subscriptions as soon as agent moves to foreground or as network connection resumes.
- "known" servers to determine whether to use SMP proxy.
- retry on SMP proxy NO_SESSION error.
- fixes to notification subscriptions.
- persistent server statistics.
- better concurrency.
SMP server:
- reduce threads usage.
- additional statistics.
- improve disabling inactive clients.
- additional control port commands for monitoring.
Notification server:
- support onion-only SMP servers.
# 5.8.2
Agent:
- fast handshake support (disabled).
- new statistics api.
SMP server:
- fast handshake support (SKEY command).
- minor changes to reduce memory usage.
# 5.8.1
Agent:
- API to reconnect one server.
- Better error handling of file errors and remote control connection errors.
- Only start uploading file once all chunks were registered on the servers.
SMP server:
- additional stats for sent message notifications.
- fix server page layout.
# 5.8.0
Version 5.8.0.10
SMP server and client:
- protocol extension to forward messages to the destination servers, to protect sending client IP address and transport session.
Agent:
- process timed out subscription responses to reduce the number of resubscriptions.
- avoid sending messages and commands when waiting for response timed out (except batched SUB and DEL commands).
- fix issue with stuck message reception on slow connection (when response to ACK timed out, and the new message was not processed until resubscribed).
- fix issue when temporary file sending or receiving error was treated as permanent.
SMP server:
- include OK responses to all batched SUB requests to reduce subscription timeouts.
XFTP server:
- report file upload timeout as TIMEOUT, to avoid delivery failure.
# 5.7.6
XFTP agent:
- treat XFTP handshake timeouts and network errors as temporary, to retry file operations.
# 5.7.5
SMP agent:
- fail if non-unique connection IDs are passed to sendMessages (to prevent client errors and deadlocks).
# 5.7.4
SMP agent:
- remove re-subscription timeouts (as they are tracked per operation, and could cause failed subscriptions).
- reconnect XFTP clients when network settings changes.
- fix lock contention resulting in stuck subscriptions on network change.
# 5.7.3
SMP/NTF protocol:
- add ALPN for handshake version negotiation, similar to XFTP (to preserve backwards compatibility with the old clients).
- upgrade clients to versions v7/v2 of the protocols.
SMP server:
- faster responses to subscription requests.
XFTP client:
- fix network exception during file download treated as permanent file error.
SMP agent:
- do not report subscription timeouts while client is offline.
# 5.7.2
SMP agent:
- fix connections failing when connecting via link due to race condition on slow network.
- remove concurrency limit when waiting for connection subscription.
- remove TLS timeout.
# 5.7.1
SMP agent:
- increase timeout for TLS connection via SOCKS
# 5.7.0
Version 5.7.0.4
_Please note_: the earliest SimpleX Chat clients supported by this version of the servers is 5.5.3 (released on February 11, 2024).
SMP server:
- increase max SMP protocol version to 7 (support for deniable authenticators).
NTF server:
- increase max NTF protocol version to 2 (support for deniable authenticators).
XFTP server:
- version handshake using ALPN.
SMP agent:
- increase timeouts for XFTP files.
- don't send commands after timeout.
- PQ encryption support.
# 5.6.2
Version 5.6.2.2.
SMP agent:
- Lower memory consumption (~20-25%).
- More stable XFTP file uploads and downloads.
- API to receive network connectivity changes from the apps.
- to reduce battery consumption: connection attempts interval growing to every 2 hours when app reports as offline.
- to reduce retries and traffic: 50% increased timeouts when on mobile network.
XFTP server:
- expire files on start.
- version negotiation based on TLS ALPN and handshake.
NTF server:
- reduced downtime by ~100x faster start time.
- exclude test tokens from statistics.
# 5.6.1
Version 5.6.1.0.
- Much faster iOS notification server start time (fewer skipped notifications).
- Fix SMP server stored message stats.
- Prevent overwriting uploaded XFTP files with subsequent upload attempts.
- Faster base64 encoding/parsing.
- Control port audit log and authentication.
# 5.6.0
Version 5.6.0.4.
SMP protocol/client/server:
- support deniable sender command authorization (to be enabled in the next version).
- remove support for SMP protocol versions (prior to v4, 07/2022).
Agent:
- optional post-quantum key agreement using sntrup761 in double ratchet protocol.
- improve performance of deleting multiple connections and files by batching database operations.
- delay connection deletion to deliver pending messages.
- API to test for notifications server.
- remove support for client protocols versions (prior to 10/2022).
XFTP server:
- restore storage quota in case of failed uploads.
Performance and stability improvements.
# 5.5.3
Agent:
- notification token API also returns active notifications server.
- support file descriptions with redirection and file URIs.
Servers:
- CLI commands for online key and certificate rotation.
- Configure config and log paths via environment variables.
# 5.5.2
Extensible handshake for clients and SMP/NTF servers (ignore extra data).
# 5.5.1
SMP servers:
- do not keep stats file open
- additional stats about currently stored messages
Agent:
- support multiple notification servers (only one can be used at a time).
- expire messages after "quota exceeded" error after 7 days (instead of 21 days previously).
- stabilize message delivery, remove unnecessary subscription retries and traffic.
- improve database performance for message delivery.
- fix sockets/memory leak - a very old bug "activated" by improvements in v5.5.0.
# 5.5.0
Code:
- compatible with GHC 8.10.7 to support compilation for armv7a.
- migrate to `crypton` from deprecated `cryptonite` (the seed for DRG is now sha512-hashed).
- use ChaChaDRG for all random IDs, keys and nonces, only using hashed entropy as seed.
- more efficient transaction batching in SMP protocol client and server.
Agent:
- stabilize message reception and delivery, migrate message delivery to database queue.
- additional event MSGNTF confirming that message received via notification is processed.
- efficient processing of messages sent to multiple recipients with batched database transactions.
- new worker abstraction for all queued tasks resilient to race conditions and some database errors.
- many fixed race conditions.
- background mode for iOS NSE.
- additional error reporting to client on critical errors (to be show as alert in the clients).
- functional api to get worker statistics.
SMP/XFTP servers:
- fix socket and memory leak on servers with high load (inactive clients without subscriptions are disconnected after set time of inactivity).
- control port improvements.
- fix statistics for stored queues, messages and files.
- make writing to store log atomic (fixes a rare bug in XFTP server).
# 5.4.0
Migrate to GHC 9.6.3
Agent:
- database improvements:
- track slow queries.
- better performance.
- "busy" error handling.
- create parent folder when needed.
- support closing and re-opening database.
- SMP agent improvements
- streaming for batched SMP commands.
- fix asynchronous JOINing connection.
- handle repeating JOINs without failure.
- api to get subscribed connections.
- return simplex:/ links as invitations.
- fix memory leak.
- XFTP improvements:
- suspend when agent is suspended.
- support locally encrypted files.
- fixes - create empty file, prevent permanent error treated as temporary.
- upgrade HTTP2 library (fixes error handling and flow control).
- Remote control protocol (XRCP)
SMP server:
- control port commands for GHC threads introspection.
- allow creating new queues without subscriptions (required for iOS).
XFTP server:
- allow 64kb file chunks.
NTF server:
- faster startup.
# 5.3.0
Agent:
- improve performance, track slow database queries.
- support delivery receipts.
SMP server:
- control port
# 5.2.0 (NTF server 1.5.0)
Agent:
- treat agent INACTIVE error as temporary - fixes failed message delivery in some race conditions.
- restore connection confirmations after client restart - fixes failed connections.
- ratchet resynchronization protocol and API.
- increase connection version to mutually supported by both peers on each received message.
Client:
- make timeout for batched functions dependent on the number of batches - fixes expiry on large batches.
Servers:
- add timeout in case of sending TCP traffic and in case of partial delivery of requested blocks to avoid resource leaks.
# 5.1.2, 5.1.3 (NTF server 1.4.1, 1.4.2)
Agent:
- ACK message on decryption error (fixes stuck message delivery bug)
- more robust connection switching logic, API to abort switching the address
Notification server:
- batch subscriptions to SMP servers
# 5.1.1 (NTF server 1.4.0)
Agent:
- store and check hashes of previous encrypted messages to differentiate between duplicates and decryption errors
Server:
- larger processing queues
- expire messages when restoring them
# 5.1.0
XFTP client:
- check encrypted file exists when uploading
- remove user ID from deletion API
Agent:
- vacuum database on migrations
SMP server:
- configure message expiration time in INI file
# 5.0.0
SimpleX File Transfer Protocol (XFTP):
- XFTP server
- XFTP CLI
- support XFTP in the agent
Other changes:
- preserve retry interval for sending messages on agent restarts
- support IPv6 in the servers and in the agent
- support for 32-bit platforms
Read more about XFTP in this post: https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html
# 4.4.1
SMP agent:
- fix handling of server addresses.
- prevent message delivery getting stuck on IO error in SMP client.
# 4.4.0
SMP agent:
- support multiple user profiles with transport session isolation.
- support optional transport isolation per connection.
- batch connection deletion
- improve asynchronous connection deletion it may now be completed after the client is restarted as well.
- improve subscription logic to retry if initial attempt fails.
- end SMP client connection after a number of failed PINGs (default is 3).
# 4.3.0
SMP server:
- additional server usage statistics.
SMP agent:
- increase retry interval when sending messages after ERR QUOTA.
# 4.2.0
SMP agent and server:
- reduce sender traffic in cases when queue quota is exceeded:
- server sends quota exceeded message to the recipient when sender receives ERR QUOTA.
- recipient sends QCONT message to the send once the queue is drained (via reply queue).
- sender retry delays are increased, reducing traffic, but sender instantly resumes delivery where QCONT is received.
SMP server:
- increase internal queue sizes.
SMP agent:
- deduplicate connection IDs in connect/disconnect responses.
- unit tests for Crypto.hs.
- fix connection switch to another queue: correctly set primary send queue.
Notification server (v1.3.0):
- check token status when sending verification notification.
# 4.1.0
SMP agent and server:
- option to toggle TLS handshake error logs (disabled by default).
SMP agent:
- include server address in BROKER error.
- api to get hash of double ratchet associated data (for connection verification).
- api to get agent statistics.
# 4.0.0
SMP server:
- Basic authentication. The server address can now include an optional password that is required to create messaging queues, so the contacts who message you will not be able to receive messages via your server, unless you share with them the address with the password. It is recommended to enable basic authentication on all private servers by adding `create_password` parameter into AUTH section of server INI file (the previously deployed servers do not have this section, you need to add it).
- Disable creating new queues completely with `new_queues: off` parameter in AUTH section of INI file - it can be used to simplify migrating the existing connections to another server.
- Updated server CLI with changed defaults:
- interactive server initialization, use -y flag to initialize the server non-interactively.
- store log is now enabled by default, so messaging queues and messages are restored when the server is restarted (to restore undelivered messages the server needs to be stopped with SIGINT signal).
- a random password is now generated by default during the server initialization.
SMP agent:
- API to test SMP servers. It connects to the server, creates and deletes a messaging queue. The new SimpleX Chat client uses this API to allow you to test that you have the correct server address, with the valid certificate fingerprint and password, before enabling the new server.
# 3.4.0
SMP agent:
- increase concurrency with connection-level locks
- fix issues identified in security assessment (see the announcement: https://github.com/simplex-chat/simplex-chat/blob/stable/blog/20221108-simplex-chat-v4.2-security-audit-new-website.md)
- manual connection queue rotation
- optional client data in connection requests links
SMP server:
- specialize monad stack to improve performance
- log slow commands
# 3.3.0
SMP server:
- allow repeated KEY command with the same key (to avoid failures on retries)
SMP agent:
- enable/disable connection notifications
- asynchronous commands that retry on network error
- use SQLCipher
# 3.2.0
SMP agent:
- Support multiple server hostnames (including onion hostnames) in server addresses.
- Network configuration options.
- Options to define rules to choose server hostname.
# 3.1.0
SMP server and agent:
- SMP protocol v4: batching multiple server commands/responses in a transport block.
# 3.0.0
SMP server:
- restore undelivered messages when the server is restarted.
- SMP protocol v3 to support push notification:
- updated SEND and MSG to add message flags (for notification flag that confirm whether the notification is sent and for any future extensions) and to move message meta-data sent to the recipient into the encrypted envelope.
- update NKEY and NID to add e2e encryption keys (for the notification meta-data encryption between SMP server and the client), and update NMSG to include this meta-data.
- update ACK command to include message ID (to avoid acknowledging unprocessed message).
- add NDEL commands to remove notification subscription credentials from SMP queue.
- add GET command to receive messages without subscription - to be used in iOS notification service extension to receive messages without terminating app subscriptions.
SMP agent:
- new protocol for duplex connection handshake reducing traffic and connection time.
- support for SMP notifications server and managing device token.
- remove redundant FQDN validation from TLS handshake to prepare for access via Tor.
- support for fully stopping agent and for temporary suspending agent operations.
- improve management of duplicate message delivery.
SMP notifications server v1.0:
- SMP notifications protocol with version negotiation during handshake.
- device token registration and verification (via background notification).
- SMP notification subscriptions and push notifications via APNS.
- restoring notification subscriptions when the server is restarted.
# 2.3.0
SMP server:
- Save and restore undelivered messages, to avoid losing them. To save messages the server has to be stopped with SIGINT signal, if it is stopped with SIGTERM undelivered messages would not be saved.
# 2.2.0
SMP server:
- Fix sockets/threads/memory leak
SMP agent:
- Support stopping and resuming agent with `disconnectAgentClient` / `resumeAgentClient`
# 2.1.1
SMP server:
- gracefully close sockets on client disconnection
- CLI warning when deleting server configuration
# 2.1.0
SMP server:
- configuration to expire inactive clients in ini file, increased TTL and check interval for client expiration
# 2.0.0
Push notifications server (beta):
- supports APNS
- manage device tokens verification via notification delivery
- sending periodic background notification to check messages (not more frequent than every 20 min)
SMP server:
- disconnect inactive clients after some period
- remove undelivered messages after 30 days
- log aggregate usage daily stats: only the number of queues created/secured/deleted/used and messages sent/delivered is logged, as one line per day, so we can plan server capacity and diagnose any problems.
SMP agent:
- manage device tokens and notification server connection
- DOWN/UP events to the agent user about server disconnections/reconnections are now sent once per server
# 1.1.0
SMP server:
- message TTL and periodic deletion of old messages
- configuration to prevent creation of the new queues
SMP agent:
- asynchronous connection handshake
- configurable SMP servers at run-time
- use TCP keep-alive for connection stability
- improve stability of connection subscriptions
- auto-vacuum DB to remove deleted records
# 1.0.3
SMP server:
- Reduce server message queue quota to 128 messages.
SMP agent:
- Add "yes to migrations" option.
- Make new SMP client attempt to reconnect on network error.
- Reduce connection handshake expiration to 2 days.
JSON encoding of types used in simplex-chat, some other minor adjustments.
# 1.0.2
General:
- Enable TLS 1.3 parameters for TLS handshake (server and client).
- Switch from hs-tls fork to original repo now that it supports getFinished and getPeerFinished APIs for both TLS 1.2 and TLS 1.3.
SMP server:
- Perform TLS handshake in a separate thread per-connection.
SMP agent:
- Cease attempts to send HELLO after one week timeout.
- Coalesce requests to connect to SMP servers, to have 1 connection per server.
# 1.0.1
SMP server:
- Explicitly set line buffering in stdout/stderr to log each line when output is redirected to files.
# 1.0.0
Security and privacy improvements:
- Faster and more secure 2-layer E2E encryption with additional encryption layer between servers and recipients:
- application messages in each duplex connection (managed by SMP agents - see [overview](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md)) are encrypted using [double-ratchet algorithm](https://www.signal.org/docs/specifications/doubleratchet/), providing forward secrecy and break-in recovery. This layer uses two Curve448 keys per client for [X3DH key agreement](https://www.signal.org/docs/specifications/x3dh/), SHA512 based HKDFs and AES-GCM AEAD encryption.
- SMP client messages are additionally E2E encrypted in each SMP queue to avoid cipher-text correlation of messages sent via multiple redundant queues (that will be supported soon). This and the next layer use [NaCl crypto_box algorithm](https://nacl.cr.yp.to/index.html) with XSalsa20Poly1305 cipher and Curve25519 keys for DH key agreement.
- Messages delivered from the servers to the recipients are additionally encrypted to avoid cipher-text correlation between sent and received messages.
- To prevent any traffic correlation by content size, SimpleX uses fixed transport block size of 16kb (16384 bytes) with padding on all encryption layers:
- application messages are padded to 15788 bytes before E2E double-ratchet encryption.
- messages between SMP clients are padded to 16032 bytes before E2E encryption in each SMP queue.
- messages from the server to the recipient are padded to padded to 16088 bytes before the additional encryption layer (see above).
- TLS 1.2+ with tls-unique channel binding in each command to prevent replay attacks.
- Server identity verification via server offline certificate fingerprints included in SMP server addresses.
New functionality:
- Support for notification servers with new SMP commands: `NKEY`/`NID`, `NSUB`/`NMSG`.
Efficiency improvements:
- Binary protocol encodings to reduce overhead from circa 15% to approximately 3.7% of transmitted application message size, with only 2.2% overhead for SMP protocol messages.
- More performant cryptographic algorithms.
For more information about SimpleX:
- [SimpleX overview](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md).
- [SimpleX chat v1 announcement](https://github.com/simplex-chat/simplex-chat/blob/master/blog/20220112-simplex-chat-v1-released.md).
# 0.5.2
- Fix message delivery logic that blocked delivery of all server messages when server per-queue quota exceeded, making it concurrent per SMP queue, not per server.
# 0.5.1
- Fix server subscription logic bug that was leading to memory leak / resource exhaustion in some edge cases.
# 0.5.0
- No changes in SMP server implementation - it is backwards compatible with v0.4.1
- SMP agent changes:
- URI syntax for SMP queues and connection requests.
- long-term connections links ("contacts") in SMP agent protocol.
- agent command changes:
- `REQ` notification and `ACPT` command are used only with long-term connection links.
- `CONF` notification and `LET` commands are used for normal duplex connections.
# 0.4.1
- Include migrations in the package
# 0.4.0
- SMP server implementation and [SMP protocol](https://github.com/simplex-chat/simplexmq/blob/master/protocol/simplex-messaging.md) changes:
- support 3072 bit RSA key size
- add SMP queue quotas
- set default transport block size to 4096 bits
- allow SMP client to change transport block size during transport connection handshake
- SMP agent implementation and protocol changes:
- additional connection confirmation step for initiating party
- automatically resume subscribed duplex connections once transport connection is resumed
- passing an arbitrary binary information between parties during the duplex connection handshake - can be used to identify parties
- asynchronous duplex connection handshake - the parties do not have to be online at the same time
- asynchronous message delivery - the agent does not need transport connection to accept client messages for delivery
- additional confirmation of message reception from the client to prevent message loss in case of process termination
- set transport block size to 8192 bits (in the future the agent protocol can allow to have different block sizes for different duplex connections)
- added client commands and notifications (see [agent protocol](https://github.com/simplex-chat/simplexmq/blob/master/protocol/agent-protocol.md)):
- `REQ` - the notification about joining party establishing connection
- `ACPT` - the command to accept connection with the joining party
- `INFO` - the notification with the information from the initiating party
- `DOWN`/`UP` - the notifications about losing/resuming the connection
- `ACK` - the command to confirm that the message reception/processing is complete
- `MID` - the response to `SEND` confirming that the message is accepted by the agent
- `MERR` - the notification about permanent message delivery error (e.g., `ERR AUTH` indicating that the queue was removed)
# 0.3.2
- Support websockets
- SMP server CLI commands
# 0.3.1
- Released to hackage.org
- SMP agent protocol changes:
- move SMP server from agent commands NEW/JOIN to agent config
- send CON to user only when the 1st party responds HELLO
- Fix REPLY vulnerability
- Fix transaction busy error
# 0.3.0
- SMP encrypted transport over TCP
- Standard X509/PKCS8 encoding for RSA keys
- Sign and verify agent messages
- Verify message integrity based on previous message hash and ID
- Prevent timing attack allowing to determine if queue exists
- Only allow correct RSA keys and signature sizes
# 0.2.0
- SMP client library
- SMP agent with E2E encryption
# 0.1.0
- SMP protocol server implementation without encryption
+85
View File
@@ -0,0 +1,85 @@
# syntax=docker/dockerfile:1.7.0-labs
ARG TAG=24.04
FROM ubuntu:${TAG} AS build
### Build stage
# Install curl and git and simplexmq dependencies
RUN apt-get update && apt-get install -y curl git build-essential libgmp3-dev zlib1g-dev llvm-18 llvm-18-dev libnuma-dev libssl-dev
# Specify bootstrap Haskell versions
ENV BOOTSTRAP_HASKELL_GHC_VERSION=9.6.3
ENV BOOTSTRAP_HASKELL_CABAL_VERSION=3.12.1.0
# Do not install Stack
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true
# Install ghcup
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
# Adjust PATH
ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH"
# Set both as default
RUN ghcup set ghc "${BOOTSTRAP_HASKELL_GHC_VERSION}" && \
ghcup set cabal "${BOOTSTRAP_HASKELL_CABAL_VERSION}"
# Copy only the source code
COPY apps /project/apps/
COPY cbits /project/cbits/
COPY src /project/src/
COPY cabal.project Setup.hs simplexmq.cabal LICENSE /project
WORKDIR /project
# Debug
#ARG CACHEBUST=1
#ADD --chmod=755 https://github.com/MShekow/directory-checksum/releases/download/v1.4.6/directory-checksum_1.4.6_linux_amd64 /usr/local/bin/directory-checksum
#RUN directory-checksum --max-depth 2 .
# Set build arguments and check if they exist
ARG APP
RUN if [ -z "$APP" ]; then printf "Please spcify \$APP build-arg.\n"; exit 1; fi
# Compile app
RUN cabal update
RUN cabal build exe:$APP
# Copy scripts
COPY scripts /project/scripts/
# Create new path containing all files needed
RUN mkdir /final
WORKDIR /final
# Strip the binary from debug symbols to reduce size
RUN bin="$(find /project/dist-newstyle -name "$APP" -type f -executable)" && \
mv "$bin" ./ && \
strip ./"$APP" &&\
mv /project/scripts/docker/entrypoint-"$APP" ./entrypoint &&\
mv /project/scripts/main/simplex-servers-stopscript ./simplex-servers-stopscript
### Final stage
FROM ubuntu:${TAG}
# Install OpenSSL dependency
RUN apt-get update && apt-get install -y openssl libnuma-dev
# Copy compiled app from build stage
COPY --from=build /final /usr/local/bin/
# Open app listening port
ARG APP_PORT
RUN if [ -z "$APP_PORT" ]; then printf "Please spcify \$APP_PORT build-arg.\n"; exit 1; fi
EXPOSE $APP_PORT
# simplexmq requires using SIGINT to correctly preserve undelivered messages and restore them on restart
STOPSIGNAL SIGINT
# Finally, execute helper script
ENTRYPOINT [ "/usr/local/bin/entrypoint" ]
+31
View File
@@ -0,0 +1,31 @@
# syntax=docker/dockerfile:1.7.0-labs
ARG TAG=24.04
FROM ubuntu:${TAG} AS build
### Build stage
ARG GHC=9.6.3
ARG CABAL=3.14.1.1
# Install curl, git and and simplexmq dependencies
RUN apt-get update && apt-get install -y curl libpq-dev git sqlite3 libsqlite3-dev build-essential libgmp3-dev zlib1g-dev llvm llvm-dev libnuma-dev libssl-dev
# Specify bootstrap Haskell versions
ENV BOOTSTRAP_HASKELL_GHC_VERSION=${GHC}
ENV BOOTSTRAP_HASKELL_CABAL_VERSION=${CABAL}
# Do not install Stack
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK=true
ENV BOOTSTRAP_HASKELL_INSTALL_NO_STACK_HOOK=true
# Install ghcup
RUN curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | BOOTSTRAP_HASKELL_NONINTERACTIVE=1 sh
# Adjust PATH
ENV PATH="/root/.cabal/bin:/root/.ghcup/bin:$PATH"
# Set both as default
RUN ghcup set ghc "${GHC}" && \
ghcup set cabal "${CABAL}"
WORKDIR /project
+292 -64
View File
@@ -1,93 +1,321 @@
# simplex-messaging
# SimpleXMQ
## SMP server demo
[![GitHub build](https://github.com/simplex-chat/simplexmq/actions/workflows/build.yml/badge.svg)](https://github.com/simplex-chat/simplexmq/actions/workflows/build.yml)
[![GitHub release](https://img.shields.io/github/v/release/simplex-chat/simplexmq)](https://github.com/simplex-chat/simplexmq/releases)
This is a demo implementation of SMP ([simplex messaging protocol](https://github.com/simplex-chat/protocol/blob/master/simplex-messaging.md)) server.
📢 SimpleXMQ v1 is released - with many security, privacy and efficiency improvements, new functionality - see [release notes](https://github.com/simplex-chat/simplexmq/releases/tag/v1.0.0).
It has a very limited utility (if any) for real applications, as it lacks the following protocol features:
**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.
- cryptographic signature verification, instead it simply compares provided "signature" with stored "public key", effectively treating them as plain text passwords.
- there is no transport encryption
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.
Because of these limitations, it is easy to experiment with the protocol logic via telnet.
## Message broker for unidirectional (simplex) queues
You can either run it locally or try with the deployed demo server:
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](./protocol/simplex-messaging.md) for client-server communication and [SMP agent protocol](./protocol/agent-protocol.md) to manage duplex connections via simplex queues on multiple SMP servers.
```bash
telnet smp.simplex.im 5223
SMP protocol is inspired by [Redis serialization protocol](https://redis.io/topics/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](https://github.com/simplex-chat/simplex-chat)).
## Components
### SMP server
[SMP server](./apps/smp-server/Main.hs) 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](./protocol/simplex-messaging.md).
#### 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:
```sh
openssl version
```
## Run locally
If it says "LibreSSL", please install original OpenSSL:
[Install stack](https://docs.haskellstack.org/en/stable/install_and_upgrade/) and `stack run`.
## Usage example
Lines you should send are prefixed with `>` character, you should not type them.
Comments are prefixed with `--`, they are not part of transmissions.
`>` on its own means you need to press `return` - telnet should be configured to send it as CRLF.
1. Create simplex message queue:
```telnet
>
>
> NEW 1234 -- 1234 is recipient's key
IDS QuCLU4YxgS7wcPFA YB4CCATREHkaQcEh -- recipient and sender IDs for the queue
```sh
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
```
2. Sender can send their "key" to the queue:
Now `openssl version` should be saying "OpenSSL". You can now run `smp-server init` to initialize your SMP server.
```telnet
> -- no signature (just press enter)
> YB4CCATREHkaQcEh -- sender ID for the queue
> SEND :key abcd
### SMP client library
YB4CCATREHkaQcEh
OK
[SMP client](./src/Simplex/Messaging/Client.hs) 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](./src/Simplex/Messaging/Agent.hs) 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](./src/Simplex/Messaging/Agent/Protocol.hs) represents SMP agent protocol to communicate via STM queues.
See [simplex-chat](https://github.com/simplex-chat/simplex-chat) terminal UI for the example of integrating SMP agent into another application.
[SMP agent executable](./apps/smp-agent/Main.hs) can be used to run a standalone SMP agent process that implements plaintext [SMP agent protocol](./protocol/agent-protocol.md) 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](https://cloud.linode.com/stackscripts/748014), 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](https://github.com/simplex-chat/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:
```sh
# For Ubuntu
apt update && apt install openssl
```
3. Secure queue with sender's "key"
### Install binaries
```telnet
> 1234 -- recipient's "signature" - same as "key" in the demo
> QuCLU4YxgS7wcPFA -- recipient ID
> KEY abcd -- "key" provided by sender
#### Using Docker
QuCLU4YxgS7wcPFA
OK
On Linux, you can deploy smp and xftp server using Docker. This will download image from [Docker Hub](https://hub.docker.com/r/simplexchat).
1. Create directories for persistent Docker configuration:
```sh
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:
```sh
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**.
```sh
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:
```sh
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
```
4. Sender can now send messages to the queue
### Build from source
```telnet
> abcd -- sender's "signature" - same as "key" in the demo
> YB4CCATREHkaQcEh -- sender ID
> SEND :hello
#### Using Docker
YB4CCATREHkaQcEh
OK
> **Please note:** to build the app use source code from [stable branch](https://github.com/simplex-chat/simplexmq/tree/stable).
On Linux, you can build smp server using Docker.
1. Build your images:
```sh
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:
```sh
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:
```sh
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**.
```sh
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):
```sh
# 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:
```sh
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`
```sh
cabal list-bin exe:smp-server
```
`xftp-server`
```sh
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`](./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](#smp-server) for more information. Run `smp-server -h` and `smp-server init -h` for explanation of commands and options.
[<img alt="Linode" src="./img/linode.svg" align="right" width="200">](https://cloud.linode.com/stackscripts/748014)
## Deploy SMP server on Linode
\* You can use free credit Linode offers when [creating a new account](https://www.linode.com/) 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](https://cloud.linode.com/stackscripts/748014) 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](https://www.linode.com/docs/guides/getting-started-with-the-linode-api#get-an-access-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](https://cloud.linode.com/domains/create) 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](https://www.linode.com/docs/guides/use-public-key-authentication-with-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](https://github.com/simplex-chat/simplexmq/issues) if any problems occur.
[<img alt="DigitalOcean" src="/img/digitalocean.png" align="right" width="300">](https://marketplace.digitalocean.com/apps/simplex-server)
## 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](https://try.digitalocean.com/freetrialoffer/) 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](https://marketplace.digitalocean.com/apps/simplex-server) 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](https://docs.digitalocean.com/products/droplets/how-to/connect-with-ssh/).
- Great, your own SMP server is ready! Use `smp://<fingerprint>@<ip_address>` as SMP server address in the client.
Please submit an [issue](https://github.com/simplex-chat/simplexmq/issues) 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](https://cloud.linode.com/stackscripts/748014) which allows this parameterization.
```sh
smp-server delete
smp-server init [-l] -n <fqdn>
```
5. Recipient recieves the message and acknowledges it to receive further messages
## SMP server design
```telnet
![SMP server design](./design/server.svg)
QuCLU4YxgS7wcPFA
MSG ECA3w3ID 2020-10-18T20:19:36.874Z 5
hello
> 1234
> QuCLU4YxgS7wcPFA
> ACK
## SMP agent design
QuCLU4YxgS7wcPFA
OK
```
![SMP agent design](./design/agent2.svg)
## Design
## License
![server design](design/server.svg)
[AGPL v3](./LICENSE)
+15
View File
@@ -0,0 +1,15 @@
{-# LANGUAGE TemplateHaskell #-}
module Web.Embedded where
import Data.FileEmbed (embedDir, embedFile)
import Simplex.Messaging.Server.Web (EmbeddedContent (..))
embeddedContent :: EmbeddedContent
embeddedContent =
EmbeddedContent
{ indexHtml = $(embedFile "apps/common/Web/static/index.html"),
linkHtml = $(embedFile "apps/common/Web/static/link.html"),
mediaContent = $(embedDir "apps/common/Web/static/media/"),
wellKnown = $(embedDir "apps/common/Web/static/.well-known/")
}
@@ -0,0 +1,49 @@
{
"applinks": {
"details": [
{
"appIDs": [
"5NN7GUYB6T.chat.simplex.app"
],
"components": [
{
"/": "/contact/*"
},
{
"/": "/contact"
},
{
"/": "/invitation/*"
},
{
"/": "/invitation"
},
{
"/": "/a/*"
},
{
"/": "/a"
},
{
"/": "/c/*"
},
{
"/": "/c"
},
{
"/": "/g/*"
},
{
"/": "/g"
},
{
"/": "/i/*"
},
{
"/": "/i"
}
]
}
]
}
}
@@ -0,0 +1,16 @@
[
{
"relation": [
"delegate_permission/common.handle_all_urls"
],
"target": {
"namespace": "android_app",
"package_name": "chat.simplex.app",
"sha256_cert_fingerprints": [
"5E:3E:DC:C2:00:FB:A8:D5:F4:88:F3:CA:4C:32:5B:05:78:C5:6A:9C:03:A1:CC:B5:92:9C:D7:5C:7E:57:E2:4D",
"3C:52:C4:FD:3C:AD:1C:07:C9:B0:0A:70:80:E3:58:FA:B9:FE:FC:B8:AF:5A:EC:14:77:65:F1:6D:0F:21:AD:85",
"AE:C1:95:DC:FD:46:14:BD:3A:91:EC:26:D1:D5:14:C8:75:71:C5:CC:8D:CF:48:08:3F:92:83:14:3C:A2:B9:A6"
]
}
}
]
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@@ -0,0 +1,26 @@
<svg width="119" height="40" viewBox="0 0 119 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.44484 39.125C8.14016 39.125 7.84284 39.1211 7.54055 39.1143C6.91433 39.1061 6.28957 39.0516 5.67141 38.9512C5.095 38.8519 4.53661 38.6673 4.01467 38.4033C3.49751 38.1415 3.02582 37.7983 2.61767 37.3867C2.20361 36.98 1.85888 36.5082 1.59716 35.9902C1.33255 35.4688 1.14942 34.9099 1.05416 34.333C0.951281 33.7131 0.895621 33.0863 0.887656 32.458C0.881316 32.2471 0.873016 31.5449 0.873016 31.5449V8.44434C0.873016 8.44434 0.881856 7.75293 0.887706 7.5498C0.895332 6.92248 0.950669 6.29665 1.05324 5.67773C1.14868 5.09925 1.33194 4.53875 1.5967 4.01563C1.85746 3.49794 2.20027 3.02586 2.61184 2.61768C3.02294 2.20562 3.49614 1.8606 4.01418 1.59521C4.53492 1.33209 5.09225 1.14873 5.6675 1.05127C6.28769 0.949836 6.91462 0.894996 7.54301 0.88721L8.44533 0.875H111.214L112.127 0.8877C112.75 0.895099 113.371 0.94945 113.985 1.05029C114.566 1.14898 115.13 1.33362 115.656 1.59814C116.694 2.13299 117.539 2.97916 118.071 4.01807C118.332 4.53758 118.512 5.09351 118.606 5.66699C118.71 6.29099 118.768 6.92174 118.78 7.5542C118.783 7.8374 118.783 8.1416 118.783 8.44434C118.791 8.81934 118.791 9.17627 118.791 9.53613V30.4648C118.791 30.8281 118.791 31.1826 118.783 31.54C118.783 31.8652 118.783 32.1631 118.779 32.4697C118.768 33.0909 118.71 33.7104 118.608 34.3232C118.515 34.9043 118.333 35.4675 118.068 35.9932C117.805 36.5056 117.462 36.9733 117.053 37.3789C116.644 37.7927 116.172 38.1379 115.653 38.4014C115.128 38.6674 114.566 38.8527 113.985 38.9512C113.367 39.0522 112.742 39.1067 112.116 39.1143C111.823 39.1211 111.517 39.125 111.219 39.125L110.135 39.127L8.44484 39.125Z" fill="black"/>
<path d="M24.7689 20.3007C24.7796 19.466 25.0013 18.6477 25.4134 17.9217C25.8254 17.1957 26.4144 16.5858 27.1254 16.1486C26.6737 15.5035 26.0778 14.9725 25.3849 14.598C24.6921 14.2234 23.9215 14.0156 23.1343 13.991C21.455 13.8147 19.8271 14.9958 18.9714 14.9958C18.0991 14.9958 16.7816 14.0085 15.3629 14.0376C14.4452 14.0673 13.5509 14.3341 12.767 14.8122C11.9831 15.2903 11.3364 15.9632 10.89 16.7655C8.95597 20.1139 10.3986 25.035 12.2512 27.7416C13.1781 29.0669 14.2613 30.5474 15.6788 30.4949C17.0659 30.4374 17.5839 29.6104 19.2582 29.6104C20.917 29.6104 21.403 30.4949 22.8492 30.4615C24.3376 30.4374 25.2753 29.1303 26.1697 27.7924C26.8357 26.848 27.3481 25.8043 27.6881 24.6999C26.8234 24.3341 26.0855 23.722 25.5664 22.9397C25.0473 22.1574 24.7699 21.2396 24.7689 20.3007V20.3007Z" fill="white"/>
<path d="M22.0373 12.2109C22.8488 11.2367 23.2486 9.98451 23.1518 8.72028C21.9119 8.8505 20.7667 9.44306 19.9442 10.3799C19.5421 10.8376 19.2341 11.37 19.0378 11.9468C18.8416 12.5235 18.7609 13.1333 18.8005 13.7413C19.4206 13.7477 20.0341 13.6132 20.5948 13.3482C21.1555 13.0831 21.6487 12.6942 22.0373 12.2109Z" fill="white"/>
<path d="M42.3023 27.1396H37.5689L36.4322 30.4961H34.4273L38.9107 18.0781H40.9937L45.4771 30.4961H43.438L42.3023 27.1396ZM38.0591 25.5908H41.8111L39.9615 20.1435H39.9097L38.0591 25.5908Z" fill="white"/>
<path d="M55.1597 25.9697C55.1597 28.7832 53.6538 30.5908 51.3814 30.5908C50.8057 30.6209 50.2332 30.4883 49.7294 30.2082C49.2256 29.928 48.8109 29.5117 48.5327 29.0068H48.4897V33.4912H46.6313V21.4424H48.4302V22.9482H48.4644C48.7553 22.4458 49.1771 22.0316 49.6847 21.7497C50.1923 21.4679 50.7669 21.3289 51.3472 21.3476C53.645 21.3477 55.1597 23.1641 55.1597 25.9697ZM53.2495 25.9697C53.2495 24.1367 52.3023 22.9316 50.857 22.9316C49.437 22.9316 48.482 24.1621 48.482 25.9697C48.482 27.7939 49.437 29.0156 50.857 29.0156C52.3023 29.0156 53.2495 27.8193 53.2495 25.9697Z" fill="white"/>
<path d="M65.1245 25.9697C65.1245 28.7832 63.6187 30.5908 61.3462 30.5908C60.7706 30.6209 60.1981 30.4883 59.6943 30.2082C59.1905 29.928 58.7758 29.5117 58.4976 29.0068H58.4546V33.4912H56.5962V21.4424H58.395V22.9482H58.4292C58.7201 22.4458 59.1419 22.0316 59.6495 21.7497C60.1571 21.4679 60.7317 21.3289 61.312 21.3476C63.6099 21.3476 65.1245 23.164 65.1245 25.9697ZM63.2144 25.9697C63.2144 24.1367 62.2671 22.9316 60.8218 22.9316C59.4019 22.9316 58.4468 24.1621 58.4468 25.9697C58.4468 27.7939 59.4019 29.0156 60.8218 29.0156C62.2671 29.0156 63.2144 27.8193 63.2144 25.9697H63.2144Z" fill="white"/>
<path d="M71.7105 27.0361C71.8482 28.2676 73.0445 29.0761 74.6792 29.0761C76.2456 29.0761 77.3726 28.2675 77.3726 27.1572C77.3726 26.1933 76.6929 25.6162 75.0835 25.2207L73.4742 24.833C71.1939 24.2822 70.1353 23.2158 70.1353 21.4853C70.1353 19.3427 72.0025 17.871 74.6538 17.871C77.2778 17.871 79.0767 19.3427 79.1372 21.4853H77.2612C77.1489 20.246 76.1245 19.498 74.6274 19.498C73.1304 19.498 72.106 20.2548 72.106 21.3564C72.106 22.2343 72.7603 22.7509 74.3608 23.1464L75.729 23.4823C78.2769 24.0849 79.3355 25.1083 79.3355 26.9247C79.3355 29.248 77.4849 30.703 74.5415 30.703C71.7876 30.703 69.9282 29.2821 69.8081 27.036L71.7105 27.0361Z" fill="white"/>
<path d="M83.3462 19.2998V21.4424H85.0679V22.9141H83.3462V27.9053C83.3462 28.6807 83.6909 29.042 84.4478 29.042C84.6522 29.0384 84.8562 29.0241 85.0591 28.999V30.4619C84.7188 30.5255 84.373 30.5543 84.0269 30.5478C82.1939 30.5478 81.479 29.8593 81.479 28.1035V22.9141H80.1626V21.4424H81.479V19.2998H83.3462Z" fill="white"/>
<path d="M86.065 25.9697C86.065 23.1211 87.7427 21.3311 90.3589 21.3311C92.9839 21.3311 94.6539 23.1211 94.6539 25.9697C94.6539 28.8262 92.9927 30.6084 90.3589 30.6084C87.7261 30.6084 86.065 28.8262 86.065 25.9697ZM92.7603 25.9697C92.7603 24.0156 91.8648 22.8623 90.3589 22.8623C88.8531 22.8623 87.9585 24.0244 87.9585 25.9697C87.9585 27.9316 88.8531 29.0762 90.3589 29.0762C91.8648 29.0762 92.7603 27.9316 92.7603 25.9697H92.7603Z" fill="white"/>
<path d="M96.1861 21.4424H97.9585V22.9834H98.0015C98.1215 22.5021 98.4034 22.0768 98.8 21.7789C99.1966 21.481 99.6836 21.3287 100.179 21.3476C100.393 21.3469 100.607 21.3702 100.816 21.417V23.1553C100.546 23.0726 100.264 23.0347 99.981 23.043C99.711 23.032 99.4419 23.0796 99.192 23.1825C98.9422 23.2854 98.7176 23.4411 98.5336 23.639C98.3496 23.8369 98.2106 24.0723 98.1262 24.3289C98.0418 24.5856 98.0139 24.8575 98.0445 25.126V30.4961H96.1861L96.1861 21.4424Z" fill="white"/>
<path d="M109.384 27.8369C109.134 29.4805 107.534 30.6084 105.486 30.6084C102.852 30.6084 101.217 28.8437 101.217 26.0127C101.217 23.1729 102.861 21.3311 105.408 21.3311C107.913 21.3311 109.488 23.0518 109.488 25.7969V26.4336H103.093V26.5459C103.064 26.8791 103.105 27.2148 103.216 27.5306C103.326 27.8464 103.502 28.1352 103.732 28.3778C103.963 28.6203 104.242 28.8111 104.552 28.9374C104.861 29.0637 105.195 29.1226 105.529 29.1103C105.968 29.1515 106.409 29.0498 106.785 28.8203C107.162 28.5909 107.455 28.246 107.62 27.8369L109.384 27.8369ZM103.102 25.1348H107.628C107.645 24.8352 107.6 24.5354 107.495 24.2541C107.39 23.9729 107.229 23.7164 107.02 23.5006C106.812 23.2849 106.561 23.1145 106.283 23.0003C106.006 22.8861 105.708 22.8305 105.408 22.8369C105.105 22.8351 104.805 22.8933 104.525 23.008C104.245 23.1227 103.99 23.2918 103.776 23.5054C103.562 23.7191 103.392 23.973 103.276 24.2527C103.16 24.5323 103.101 24.8321 103.102 25.1348V25.1348Z" fill="white"/>
<path d="M37.8262 8.73101C38.2158 8.70305 38.6068 8.76191 38.9709 8.90335C39.335 9.04478 39.6632 9.26526 39.9318 9.54889C40.2004 9.83251 40.4026 10.1722 40.524 10.5435C40.6455 10.9148 40.6829 11.3083 40.6338 11.6959C40.6338 13.6021 39.6035 14.6979 37.8262 14.6979H35.6709V8.73101H37.8262ZM36.5977 13.854H37.7227C38.0011 13.8707 38.2797 13.825 38.5382 13.7204C38.7968 13.6158 39.0287 13.4548 39.2172 13.2493C39.4057 13.0437 39.546 12.7987 39.6279 12.5321C39.7097 12.2655 39.7311 11.9839 39.6904 11.708C39.7282 11.4332 39.7046 11.1534 39.6215 10.8887C39.5384 10.6241 39.3977 10.3811 39.2097 10.1771C39.0216 9.97322 38.7908 9.81341 38.5337 9.70917C38.2766 9.60494 37.9997 9.55885 37.7227 9.57422H36.5977V13.854Z" fill="white"/>
<path d="M41.6807 12.4443C41.6524 12.1484 41.6862 11.8499 41.7801 11.5678C41.8739 11.2857 42.0257 11.0264 42.2256 10.8064C42.4255 10.5864 42.6693 10.4107 42.9411 10.2904C43.213 10.1701 43.507 10.108 43.8042 10.108C44.1015 10.108 44.3955 10.1701 44.6673 10.2904C44.9392 10.4107 45.1829 10.5864 45.3828 10.8064C45.5828 11.0264 45.7345 11.2857 45.8284 11.5678C45.9222 11.8499 45.9561 12.1484 45.9278 12.4443C45.9566 12.7406 45.9232 13.0396 45.8296 13.3221C45.736 13.6046 45.5843 13.8644 45.3843 14.0848C45.1843 14.3052 44.9404 14.4814 44.6683 14.6019C44.3962 14.7225 44.1018 14.7847 43.8042 14.7847C43.5066 14.7847 43.2123 14.7225 42.9401 14.6019C42.668 14.4814 42.4241 14.3052 42.2241 14.0848C42.0241 13.8644 41.8725 13.6046 41.7789 13.3221C41.6853 13.0396 41.6518 12.7406 41.6807 12.4443V12.4443ZM45.0137 12.4443C45.0137 11.4683 44.5752 10.8975 43.8057 10.8975C43.0332 10.8975 42.5987 11.4683 42.5987 12.4444C42.5987 13.4282 43.0333 13.9946 43.8057 13.9946C44.5752 13.9946 45.0137 13.4243 45.0137 12.4443H45.0137Z" fill="white"/>
<path d="M51.5733 14.6978H50.6514L49.7207 11.3813H49.6504L48.7237 14.6978H47.8106L46.5694 10.1948H47.4707L48.2774 13.6308H48.3438L49.2696 10.1948H50.1221L51.0479 13.6308H51.1182L51.9209 10.1948H52.8096L51.5733 14.6978Z" fill="white"/>
<path d="M53.8536 10.1948H54.709V10.9102H54.7754C54.8881 10.6532 55.0781 10.4379 55.319 10.2941C55.5598 10.1503 55.8396 10.0852 56.1192 10.1079C56.3383 10.0914 56.5583 10.1245 56.7629 10.2046C56.9675 10.2847 57.1514 10.4098 57.3011 10.5706C57.4508 10.7315 57.5624 10.9239 57.6276 11.1337C57.6928 11.3436 57.7099 11.5654 57.6778 11.7827V14.6977H56.7891V12.0059C56.7891 11.2822 56.4746 10.9224 55.8174 10.9224C55.6687 10.9154 55.5202 10.9408 55.3821 10.9966C55.244 11.0524 55.1197 11.1375 55.0176 11.2458C54.9154 11.3542 54.838 11.4834 54.7904 11.6245C54.7429 11.7657 54.7265 11.9154 54.7422 12.0635V14.6978H53.8535L53.8536 10.1948Z" fill="white"/>
<path d="M59.0938 8.43701H59.9825V14.6978H59.0938V8.43701Z" fill="white"/>
<path d="M61.2178 12.4443C61.1895 12.1484 61.2234 11.8498 61.3172 11.5677C61.4111 11.2857 61.5629 11.0263 61.7629 10.8063C61.9628 10.5863 62.2065 10.4106 62.4784 10.2903C62.7503 10.17 63.0443 10.1079 63.3416 10.1079C63.6389 10.1079 63.9329 10.17 64.2047 10.2903C64.4766 10.4106 64.7203 10.5863 64.9203 10.8063C65.1203 11.0263 65.272 11.2857 65.3659 11.5677C65.4598 11.8498 65.4936 12.1484 65.4654 12.4443C65.4942 12.7406 65.4607 13.0396 65.3671 13.3221C65.2734 13.6046 65.1218 13.8644 64.9217 14.0848C64.7217 14.3052 64.4778 14.4814 64.2057 14.6019C63.9335 14.7224 63.6392 14.7847 63.3416 14.7847C63.044 14.7847 62.7496 14.7224 62.4775 14.6019C62.2053 14.4814 61.9614 14.3052 61.7614 14.0848C61.5614 13.8644 61.4097 13.6046 61.3161 13.3221C61.2225 13.0396 61.189 12.7406 61.2178 12.4443V12.4443ZM64.5508 12.4443C64.5508 11.4683 64.1123 10.8975 63.3428 10.8975C62.5703 10.8975 62.1358 11.4683 62.1358 12.4444C62.1358 13.4282 62.5704 13.9946 63.3428 13.9946C64.1123 13.9946 64.5508 13.4243 64.5508 12.4443H64.5508Z" fill="white"/>
<path d="M66.4009 13.4243C66.4009 12.6138 67.0044 12.1465 68.0757 12.0801L69.2954 12.0098V11.6211C69.2954 11.1455 68.981 10.877 68.3736 10.877C67.8775 10.877 67.5337 11.0591 67.4351 11.3775H66.5747C66.6656 10.604 67.3931 10.1079 68.4146 10.1079C69.5435 10.1079 70.1802 10.6699 70.1802 11.6211V14.6978H69.3247V14.065H69.2544C69.1117 14.292 68.9113 14.477 68.6737 14.6012C68.4361 14.7254 68.1697 14.7844 67.9019 14.772C67.7129 14.7916 67.5218 14.7715 67.341 14.7128C67.1603 14.6541 66.9938 14.5581 66.8524 14.4312C66.711 14.3042 66.5977 14.149 66.52 13.9756C66.4422 13.8022 66.4017 13.6144 66.4009 13.4243V13.4243ZM69.2954 13.0396V12.6631L68.1958 12.7334C67.5757 12.7749 67.2945 12.9859 67.2945 13.3828C67.2945 13.7881 67.646 14.0239 68.1295 14.0239C68.2711 14.0383 68.4142 14.024 68.5502 13.9819C68.6862 13.9398 68.8123 13.8708 68.9211 13.7789C69.0299 13.6871 69.1191 13.5743 69.1834 13.4473C69.2477 13.3203 69.2858 13.1816 69.2954 13.0396V13.0396Z" fill="white"/>
<path d="M71.3482 12.4444C71.3482 11.0215 72.0796 10.1201 73.2173 10.1201C73.4987 10.1072 73.778 10.1746 74.0226 10.3145C74.2671 10.4544 74.4667 10.661 74.5982 10.9101H74.6646V8.43701H75.5533V14.6978H74.7017V13.9863H74.6314C74.4898 14.2338 74.2832 14.4378 74.0339 14.5763C73.7847 14.7148 73.5023 14.7825 73.2173 14.772C72.0718 14.772 71.3482 13.8706 71.3482 12.4444ZM72.2662 12.4444C72.2662 13.3994 72.7164 13.9741 73.4693 13.9741C74.2183 13.9741 74.6812 13.3911 74.6812 12.4483C74.6812 11.5098 74.2134 10.9185 73.4693 10.9185C72.7212 10.9185 72.2661 11.4971 72.2661 12.4444H72.2662Z" fill="white"/>
<path d="M79.23 12.4443C79.2017 12.1484 79.2356 11.8499 79.3294 11.5678C79.4232 11.2857 79.575 11.0264 79.7749 10.8064C79.9749 10.5864 80.2186 10.4107 80.4904 10.2904C80.7623 10.1701 81.0563 10.108 81.3536 10.108C81.6508 10.108 81.9448 10.1701 82.2167 10.2904C82.4885 10.4107 82.7322 10.5864 82.9322 10.8064C83.1321 11.0264 83.2839 11.2857 83.3777 11.5678C83.4715 11.8499 83.5054 12.1484 83.4771 12.4443C83.5059 12.7406 83.4725 13.0396 83.3789 13.3221C83.2853 13.6046 83.1336 13.8644 82.9336 14.0848C82.7336 14.3052 82.4897 14.4814 82.2176 14.6019C81.9455 14.7225 81.6512 14.7847 81.3536 14.7847C81.0559 14.7847 80.7616 14.7225 80.4895 14.6019C80.2173 14.4814 79.9735 14.3052 79.7735 14.0848C79.5735 13.8644 79.4218 13.6046 79.3282 13.3221C79.2346 13.0396 79.2012 12.7406 79.23 12.4443V12.4443ZM82.563 12.4443C82.563 11.4683 82.1245 10.8975 81.355 10.8975C80.5826 10.8975 80.148 11.4683 80.148 12.4444C80.148 13.4282 80.5826 13.9946 81.355 13.9946C82.1245 13.9946 82.563 13.4243 82.563 12.4443Z" fill="white"/>
<path d="M84.6695 10.1948H85.5249V10.9102H85.5913C85.704 10.6532 85.894 10.4379 86.1349 10.2941C86.3757 10.1503 86.6555 10.0852 86.9351 10.1079C87.1542 10.0914 87.3742 10.1245 87.5788 10.2046C87.7834 10.2847 87.9673 10.4098 88.117 10.5706C88.2667 10.7315 88.3783 10.9239 88.4435 11.1337C88.5087 11.3436 88.5258 11.5654 88.4937 11.7827V14.6977H87.605V12.0059C87.605 11.2822 87.2906 10.9224 86.6333 10.9224C86.4846 10.9154 86.3361 10.9408 86.198 10.9966C86.06 11.0524 85.9356 11.1375 85.8335 11.2458C85.7314 11.3542 85.6539 11.4834 85.6064 11.6245C85.5588 11.7657 85.5424 11.9154 85.5581 12.0635V14.6978H84.6695V10.1948Z" fill="white"/>
<path d="M93.5152 9.07373V10.2153H94.4908V10.9639H93.5152V13.2793C93.5152 13.751 93.7095 13.9575 94.1519 13.9575C94.2651 13.9572 94.3783 13.9503 94.4908 13.937V14.6772C94.3312 14.7058 94.1695 14.721 94.0074 14.7226C93.0191 14.7226 92.6255 14.375 92.6255 13.5068V10.9638H91.9107V10.2153H92.6255V9.07373H93.5152Z" fill="white"/>
<path d="M95.7046 8.43701H96.5855V10.9185H96.6558C96.7739 10.6591 96.9691 10.4425 97.2148 10.2982C97.4605 10.1539 97.7448 10.0888 98.0288 10.1118C98.2467 10.1 98.4646 10.1364 98.6669 10.2184C98.8692 10.3004 99.0509 10.4261 99.199 10.5864C99.3471 10.7468 99.458 10.9378 99.5238 11.146C99.5896 11.3541 99.6086 11.5742 99.5796 11.7905V14.6978H98.69V12.0098C98.69 11.2905 98.355 10.9263 97.7271 10.9263C97.5744 10.9137 97.4207 10.9347 97.277 10.9878C97.1332 11.0408 97.0027 11.1247 96.8948 11.2334C96.7868 11.3421 96.7038 11.4732 96.6518 11.6173C96.5997 11.7614 96.5798 11.9152 96.5933 12.0679V14.6977H95.7047L95.7046 8.43701Z" fill="white"/>
<path d="M104.761 13.4819C104.641 13.8935 104.379 14.2495 104.022 14.4876C103.666 14.7258 103.236 14.8309 102.81 14.7847C102.513 14.7925 102.219 14.7357 101.946 14.6182C101.674 14.5006 101.43 14.3252 101.232 14.1041C101.034 13.8829 100.887 13.6214 100.8 13.3376C100.713 13.0537 100.689 12.7544 100.73 12.4605C100.691 12.1656 100.715 11.8656 100.801 11.581C100.888 11.2963 101.034 11.0335 101.231 10.8105C101.428 10.5874 101.671 10.4092 101.942 10.288C102.214 10.1668 102.509 10.1054 102.806 10.1079C104.059 10.1079 104.815 10.9639 104.815 12.3779V12.688H101.635V12.7378C101.621 12.9031 101.642 13.0694 101.696 13.2261C101.75 13.3829 101.837 13.5266 101.95 13.6481C102.062 13.7695 102.2 13.866 102.352 13.9314C102.504 13.9968 102.669 14.0297 102.835 14.0278C103.047 14.0533 103.262 14.0151 103.453 13.9178C103.644 13.8206 103.802 13.6689 103.906 13.4819L104.761 13.4819ZM101.635 12.0308H103.91C103.921 11.8796 103.9 11.7278 103.849 11.5851C103.798 11.4424 103.718 11.3119 103.614 11.2021C103.509 11.0922 103.383 11.0054 103.243 10.9472C103.103 10.8891 102.953 10.8608 102.801 10.8643C102.648 10.8623 102.495 10.8912 102.353 10.9491C102.21 11.0071 102.081 11.0929 101.972 11.2017C101.864 11.3104 101.778 11.4397 101.72 11.5821C101.662 11.7245 101.633 11.8771 101.635 12.0308H101.635Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

+77
View File
@@ -0,0 +1,77 @@
(function () {
let complete = false
run()
window.onload = run
async function run() {
const connURIel = document.getElementById("conn_req_uri_text");
const mobileConnURIanchor = document.getElementById("mobile_conn_req_uri");
const connQRCodes = document.getElementsByClassName("conn_req_uri_qrcode");
console.log(connQRCodes);
if (complete || !connURIel || !mobileConnURIanchor || connQRCodes < 2) return
complete = true
let connURI = document.location.toString()
const parsedURI = new URL(connURI)
const path = parsedURI.pathname.split("/")
const len = path.length
const action = path[len - (path[len - 1] == "" ? 2 : 1)]
parsedURI.protocol = "https"
parsedURI.pathname = "/" + action
connURI = parsedURI.toString()
console.log("connection URI: ", connURI)
const hash = parsedURI.hash
const hostname = parsedURI.hostname
let appURI = "simplex:" + parsedURI.pathname
appURI += action.length > 1 // not short link
? hash
: !hash.includes("?") // otherwise add server hostname
? hash + "?h=" + hostname // no parameters
: !hash.includes("?h=") && !hash.includes("&h=")
? hash + "&h=" + hostname // no "h" parameter
: hash.replace(/([?&])h=([^&]+)/, `$1h=${hostname},$2`) // add as the first hostname to "h" parameter
mobileConnURIanchor.href = appURI
console.log("app URI: ", appURI)
connURIel.innerText = "/c " + connURI
for (const connQRCode of connQRCodes) {
try {
await QRCode.toCanvas(connQRCode, connURI, {
errorCorrectionLevel: "M",
color: {dark: "#062D56"}
});
connQRCode.style.width = "320px";
connQRCode.style.height = "320px";
} catch (err) {
console.error(err);
}
}
function contentCopyWithTooltip(parent) {
const content = parent.querySelector(".content");
const tooltip = parent.querySelector(".tooltiptext");
console.log(parent.querySelector(".content_copy"), 111)
console.log(parent)
const copyButton = parent.querySelector(".content_copy");
copyButton.addEventListener("click", copyAddress)
copyButton.addEventListener("mouseout", resetTooltip)
function copyAddress() {
navigator.clipboard.writeText(content.innerText || content.value);
tooltip.innerHTML = "Copied!";
}
function resetTooltip() {
tooltip.innerHTML = "Copy to clipboard";
}
}
function copyAddress() {
navigator.clipboard.writeText(connURI);
tooltipEl.innerHTML = "Copied!";
}
function resetTooltip() {
tooltipEl.innerHTML = "Copy to clipboard";
}
}
})();
Binary file not shown.

After

Width:  |  Height:  |  Size: 289 KiB

+372
View File
@@ -0,0 +1,372 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="560"
height="164"
version="1.1"
id="svg1048"
sodipodi:docname="f_droid.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1050"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:snap-bbox="true"
inkscape:bbox-paths="true"
inkscape:bbox-nodes="true"
inkscape:snap-bbox-edge-midpoints="true"
inkscape:snap-bbox-midpoints="true"
inkscape:snap-intersection-paths="true"
inkscape:object-paths="true"
inkscape:snap-smooth-nodes="true"
inkscape:snap-midpoints="true"
inkscape:snap-object-midpoints="true"
inkscape:snap-center="true"
inkscape:snap-text-baseline="true"
inkscape:snap-page="true"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.1996734"
inkscape:cx="186.71748"
inkscape:cy="100.02722"
inkscape:window-width="1920"
inkscape:window-height="1007"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1048" />
<defs
id="defs974">
<linearGradient
id="a">
<stop
offset="0"
stop-color="#fff"
stop-opacity=".098"
id="stop968" />
<stop
offset="1"
stop-color="#fff"
stop-opacity="0"
id="stop970" />
</linearGradient>
<radialGradient
xlink:href="#a"
id="b"
cx="113"
cy="-12.89"
fx="113"
fy="-12.89"
r="59.661999"
gradientTransform="matrix(0,1.96105,-1.97781,0,254.507,78.763)"
gradientUnits="userSpaceOnUse" />
</defs>
<g
transform="translate(-332,-355.362)"
id="g1046">
<rect
style="stroke:none;marker:none"
width="560"
height="164"
x="332"
y="355.362"
rx="20"
ry="20"
color="#000000"
overflow="visible"
stroke="#a6a6a6"
stroke-width="4"
id="rect976" />
<text
y="402.367"
x="508.95099"
style="line-height:100%;-inkscape-font-specification:'DejaVu Sans';marker:none"
color="#000000"
font-weight="400"
font-size="12.395px"
font-family="'DejaVu Sans'"
letter-spacing="0"
word-spacing="0"
overflow="visible"
fill="#ffffff"
id="text980"><tspan
y="402.367"
x="508.95099"
style="-inkscape-font-specification:'DejaVu Sans'"
font-size="34.125px"
id="tspan978">GET IT ON</tspan></text>
<text
style="line-height:100%;-inkscape-font-specification:'Rokkitt Bold';text-align:start;marker:none"
x="508.21301"
y="489.36099"
color="#000000"
font-weight="700"
font-size="29.709px"
font-family="Rokkitt"
letter-spacing="0"
word-spacing="0"
overflow="visible"
fill="#ffffff"
id="text984"><tspan
x="508.21301"
y="489.36099"
style="line-height:100%;-inkscape-font-specification:'Roboto Slab Bold';text-align:start"
font-size="95px"
font-family="'Roboto Slab'"
id="tspan982">F-Droid</tspan></text>
<g
fill-rule="evenodd"
id="g994">
<path
d="m 2.589,1006.862 4.25,5.5"
fill="#8ab000"
stroke="#769616"
stroke-width="2.5"
stroke-linecap="round"
transform="matrix(-2.63159,0,0,2.63157,483.158,-2270.475)"
id="path986" />
<path
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
d="m 476.286,375.862 c 1.193,0.031 2.004,0.497 2.58,1.18 -5.333,6.34 -6.232,7.347 -13.514,16.372 -2.683,3.472 -5.478,1.678 -2.795,-1.793 l 11.185,-14.474 c 0.602,-0.804 1.54,-1.258 2.544,-1.285 z"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#ffffff"
fill-opacity="0.298"
id="path988" />
<path
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
d="m 478.89,377.075 c 0.325,0.39 1.476,2.118 0.058,4.096 l -11.184,14.473 c -2.683,3.471 -3.026,-1.611 -3.026,-1.611 0,0 9.828,-11.869 14.151,-16.958 z"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#263238"
fill-opacity="0.2"
id="path990" />
<path
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
d="m 477.006,376.48 c 1.153,0 2.525,0.373 2.169,2.102 -0.273,1.32 -12.266,15.985 -12.266,15.985 -2.683,3.47 -6.562,1.78 -3.879,-1.691 l 11.143,-14.402 c 0.685,-0.763 1.602,-1.957 2.833,-1.994 z"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#8ab000"
id="path992" />
</g>
<g
fill-rule="evenodd"
id="g1004">
<path
d="m 2.589,1006.862 4.25,5.5"
fill="#8ab000"
stroke="#769616"
stroke-width="2.5"
stroke-linecap="round"
transform="matrix(2.63159,0,0,2.63157,356.842,-2270.475)"
id="path996" />
<path
d="m 363.714,375.862 c -1.193,0.031 -2.004,0.497 -2.58,1.18 5.333,6.34 6.232,7.347 13.514,16.372 2.683,3.472 5.478,1.678 2.795,-1.793 l -11.185,-14.474 c -0.602,-0.804 -1.54,-1.258 -2.544,-1.285 z"
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#ffffff"
fill-opacity="0.298"
id="path998" />
<path
d="m 361.11,377.075 c -0.325,0.39 -1.476,2.118 -0.058,4.096 l 11.184,14.473 c 2.683,3.471 3.026,-1.611 3.026,-1.611 0,0 -9.828,-11.869 -14.151,-16.958 z"
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#263238"
fill-opacity="0.2"
id="path1000" />
<path
d="m 362.995,376.48 c -1.153,0 -2.526,0.373 -2.17,2.102 0.273,1.32 12.266,15.985 12.266,15.985 2.683,3.47 6.562,1.78 3.879,-1.691 l -11.143,-14.402 c -0.685,-0.763 -1.602,-1.957 -2.832,-1.994 z"
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#8ab000"
id="path1002" />
</g>
<g
transform="matrix(2.63159,0,0,2.63157,467.369,-2270.475)"
id="g1014">
<rect
ry="3"
rx="3"
y="1010.36"
x="-37"
height="12.92"
width="38"
fill="#aeea00"
id="rect1006" />
<rect
width="38"
height="10"
x="-37"
y="1013.279"
rx="3"
ry="3"
fill="#263238"
fill-opacity="0.2"
id="rect1008" />
<rect
width="38"
height="10"
x="-37"
y="1010.362"
rx="3"
ry="3"
fill="#ffffff"
fill-opacity="0.298"
id="rect1010" />
<rect
width="38"
height="10.641"
x="-37"
y="1011.5"
rx="3"
ry="2.4560001"
fill="#aeea00"
id="rect1012" />
</g>
<g
transform="matrix(2.63159,0,0,2.63157,356.842,-2270.745)"
id="g1024">
<rect
ry="3"
rx="3"
y="1024.522"
x="5"
height="25.84"
width="38"
fill="#1976d2"
id="rect1016" />
<rect
width="38"
height="13"
x="5"
y="1037.3621"
rx="3"
ry="3"
fill="#263238"
fill-opacity="0.2"
id="rect1018" />
<rect
width="38"
height="13"
x="5"
y="1024.442"
rx="3"
ry="3"
fill="#ffffff"
fill-opacity="0.2"
id="rect1020" />
<rect
width="38"
height="23.559999"
x="5"
y="1025.662"
rx="3"
ry="2.7179999"
fill="#1976d2"
id="rect1022" />
</g>
<g
transform="matrix(2.63159,0,0,2.63157,356.842,396.264)"
id="g1030">
<path
d="m 24,17.75 c -2.88,0 -5.32,1.985 -6.033,4.65 H 21.18 A 3.215,3.215 0 0 1 24,20.75 3.228,3.228 0 0 1 27.25,24 3.228,3.228 0 0 1 24,27.25 3.219,3.219 0 0 1 21.07,25.4 h -3.154 c 0.642,2.766 3.132,4.85 6.084,4.85 3.434,0 6.25,-2.816 6.25,-6.25 0,-3.434 -2.816,-6.25 -6.25,-6.25 z"
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="#0d47a1"
id="path1026" />
<circle
r="9.5500002"
cy="24"
cx="24"
fill="none"
stroke="#0d47a1"
stroke-width="1.9"
stroke-linecap="round"
id="circle1028" />
</g>
<g
transform="matrix(2.63159,0,0,2.63157,356.842,-2269.159)"
id="g1036">
<ellipse
ry="3.875"
rx="3.375"
cx="14.375"
cy="1016.487"
fill="#263238"
fill-opacity="0.2"
id="ellipse1032" />
<circle
r="3.375"
cy="1016.987"
cx="14.375"
fill="#ffffff"
id="circle1034" />
</g>
<g
transform="matrix(2.63159,0,0,2.63157,408.158,-2269.159)"
id="g1042">
<ellipse
cy="1016.487"
cx="14.375"
rx="3.375"
ry="3.875"
fill="#263238"
fill-opacity="0.2"
id="ellipse1038" />
<circle
cx="14.375"
cy="1016.987"
r="3.375"
fill="#ffffff"
id="circle1040" />
</g>
<path
d="m 282.715,299.835 a 3.29,3.29 0 0 0 -2.662,5.336 l 9.474,12.261 A 7.894,7.894 0 0 0 289,320.257 v 18.21 a 7.877,7.877 0 0 0 7.895,7.895 h 84.21 A 7.877,7.877 0 0 0 389,338.468 v -18.211 c 0,-0.999 -0.19,-1.949 -0.525,-2.826 l 9.472,-12.26 a 3.29,3.29 0 0 0 -2.433,-5.334 3.29,3.29 0 0 0 -2.772,1.31 l -9.013,11.666 a 7.91,7.91 0 0 0 -2.624,-0.45 h -84.21 c -0.922,0 -1.8,0.163 -2.622,0.45 l -9.015,-11.666 a 3.29,3.29 0 0 0 -2.543,-1.312 z m 14.18,49.527 A 7.877,7.877 0 0 0 289,357.257 v 52.21 a 7.877,7.877 0 0 0 7.895,7.895 h 84.21 A 7.877,7.877 0 0 0 389,409.468 v -52.211 a 7.877,7.877 0 0 0 -7.895,-7.895 z"
style="line-height:normal;text-indent:0;text-align:start;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;text-transform:none;isolation:auto;mix-blend-mode:normal;fill:url(#b)"
color="#000000"
font-weight="400"
font-family="sans-serif"
white-space="normal"
overflow="visible"
fill="url(#b)"
fill-rule="evenodd"
transform="translate(81,76)"
id="path1044" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,39 @@
<svg width="135" height="41" viewBox="0 0 135 41" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M130 40.5H5C2.2 40.5 0 38.3 0 35.5V5.5C0 2.7 2.2 0.5 5 0.5H130C132.8 0.5 135 2.7 135 5.5V35.5C135 38.3 132.8 40.5 130 40.5Z" fill="black"/>
<path d="M47.4 10.7C47.4 11.5 47.2 12.2 46.7 12.7C46.1 13.3 45.4 13.6 44.5 13.6C43.6 13.6 42.9 13.3 42.3 12.7C41.7 12.1 41.4 11.4 41.4 10.5C41.4 9.60002 41.7 8.90002 42.3 8.30002C42.9 7.70002 43.6 7.40002 44.5 7.40002C44.9 7.40002 45.3 7.50002 45.7 7.70002C46.1 7.90002 46.4 8.10002 46.6 8.40002L46.1 8.90002C45.7 8.40002 45.2 8.20002 44.5 8.20002C43.9 8.20002 43.3 8.40002 42.9 8.90002C42.4 9.30002 42.2 9.90002 42.2 10.6C42.2 11.3 42.4 11.9 42.9 12.3C43.4 12.7 43.9 13 44.5 13C45.2 13 45.7 12.8 46.2 12.3C46.5 12 46.7 11.6 46.7 11.1H44.5V10.3H47.4V10.7V10.7ZM52 8.20002H49.3V10.1H51.8V10.8H49.3V12.7H52V13.5H48.5V7.50002H52V8.20002ZM55.3 13.5H54.5V8.20002H52.8V7.50002H57V8.20002H55.3V13.5ZM59.9 13.5V7.50002H60.7V13.5H59.9ZM64.1 13.5H63.3V8.20002H61.6V7.50002H65.7V8.20002H64V13.5H64.1ZM73.6 12.7C73 13.3 72.3 13.6 71.4 13.6C70.5 13.6 69.8 13.3 69.2 12.7C68.6 12.1 68.3 11.4 68.3 10.5C68.3 9.60002 68.6 8.90002 69.2 8.30002C69.8 7.70002 70.5 7.40002 71.4 7.40002C72.3 7.40002 73 7.70002 73.6 8.30002C74.2 8.90002 74.5 9.60002 74.5 10.5C74.5 11.4 74.2 12.1 73.6 12.7ZM69.8 12.2C70.2 12.6 70.8 12.9 71.4 12.9C72 12.9 72.6 12.7 73 12.2C73.4 11.8 73.7 11.2 73.7 10.5C73.7 9.80002 73.5 9.20002 73 8.80002C72.6 8.40002 72 8.10002 71.4 8.10002C70.8 8.10002 70.2 8.30002 69.8 8.80002C69.4 9.20002 69.1 9.80002 69.1 10.5C69.1 11.2 69.3 11.8 69.8 12.2ZM75.6 13.5V7.50002H76.5L79.4 12.2V7.50002H80.2V13.5H79.4L76.3 8.60002V13.5H75.6V13.5Z" fill="white" stroke="white" stroke-width="0.2" stroke-miterlimit="10"/>
<path d="M68.1 22.3C65.7 22.3 63.8 24.1 63.8 26.6C63.8 29 65.7 30.9 68.1 30.9C70.5 30.9 72.4 29.1 72.4 26.6C72.4 24 70.5 22.3 68.1 22.3ZM68.1 29.1C66.8 29.1 65.7 28 65.7 26.5C65.7 25 66.8 23.9 68.1 23.9C69.4 23.9 70.5 24.9 70.5 26.5C70.5 28 69.4 29.1 68.1 29.1ZM58.8 22.3C56.4 22.3 54.5 24.1 54.5 26.6C54.5 29 56.4 30.9 58.8 30.9C61.2 30.9 63.1 29.1 63.1 26.6C63.1 24 61.2 22.3 58.8 22.3ZM58.8 29.1C57.5 29.1 56.4 28 56.4 26.5C56.4 25 57.5 23.9 58.8 23.9C60.1 23.9 61.2 24.9 61.2 26.5C61.2 28 60.1 29.1 58.8 29.1ZM47.7 23.6V25.4H52C51.9 26.4 51.5 27.2 51 27.7C50.4 28.3 49.4 29 47.7 29C45 29 43 26.9 43 24.2C43 21.5 45.1 19.4 47.7 19.4C49.1 19.4 50.2 20 51 20.7L52.3 19.4C51.2 18.4 49.8 17.6 47.8 17.6C44.2 17.6 41.1 20.6 41.1 24.2C41.1 27.8 44.2 30.8 47.8 30.8C49.8 30.8 51.2 30.2 52.4 28.9C53.6 27.7 54 26 54 24.7C54 24.3 54 23.9 53.9 23.6H47.7V23.6ZM93.1 25C92.7 24 91.7 22.3 89.5 22.3C87.3 22.3 85.5 24 85.5 26.6C85.5 29 87.3 30.9 89.7 30.9C91.6 30.9 92.8 29.7 93.2 29L91.8 28C91.3 28.7 90.7 29.2 89.7 29.2C88.7 29.2 88.1 28.8 87.6 27.9L93.3 25.5L93.1 25V25ZM87.3 26.4C87.3 24.8 88.6 23.9 89.5 23.9C90.2 23.9 90.9 24.3 91.1 24.8L87.3 26.4ZM82.6 30.5H84.5V18H82.6V30.5ZM79.6 23.2C79.1 22.7 78.3 22.2 77.3 22.2C75.2 22.2 73.2 24.1 73.2 26.5C73.2 28.9 75.1 30.7 77.3 30.7C78.3 30.7 79.1 30.2 79.5 29.7H79.6V30.3C79.6 31.9 78.7 32.8 77.3 32.8C76.2 32.8 75.4 32 75.2 31.3L73.6 32C74.1 33.1 75.3 34.5 77.4 34.5C79.6 34.5 81.4 33.2 81.4 30.1V22.5H79.6V23.2V23.2ZM77.4 29.1C76.1 29.1 75 28 75 26.5C75 25 76.1 23.9 77.4 23.9C78.7 23.9 79.7 25 79.7 26.5C79.7 28 78.7 29.1 77.4 29.1ZM101.8 18H97.3V30.5H99.2V25.8H101.8C103.9 25.8 105.9 24.3 105.9 21.9C105.9 19.5 103.9 18 101.8 18V18ZM101.9 24H99.2V19.7H101.9C103.3 19.7 104.1 20.9 104.1 21.8C104 22.9 103.2 24 101.9 24ZM113.4 22.2C112 22.2 110.6 22.8 110.1 24.1L111.8 24.8C112.2 24.1 112.8 23.9 113.5 23.9C114.5 23.9 115.4 24.5 115.5 25.5V25.6C115.2 25.4 114.4 25.1 113.6 25.1C111.8 25.1 110 26.1 110 27.9C110 29.6 111.5 30.7 113.1 30.7C114.4 30.7 115 30.1 115.5 29.5H115.6V30.5H117.4V25.7C117.2 23.5 115.5 22.2 113.4 22.2V22.2ZM113.2 29.1C112.6 29.1 111.7 28.8 111.7 28C111.7 27 112.8 26.7 113.7 26.7C114.5 26.7 114.9 26.9 115.4 27.1C115.2 28.3 114.2 29.1 113.2 29.1V29.1ZM123.7 22.5L121.6 27.9H121.5L119.3 22.5H117.3L120.6 30.1L118.7 34.3H120.6L125.7 22.5H123.7V22.5ZM106.9 30.5H108.8V18H106.9V30.5Z" fill="white"/>
<path d="M10.4 8C10.1 8.3 10 8.8 10 9.4V31.5C10 32.1 10.2 32.6 10.5 32.9L10.6 33L23 20.6V20.4L10.4 8Z" fill="url(#paint0_linear_7_408)"/>
<path d="M27 24.8L22.9 20.7V20.4L27 16.3L27.1 16.4L32 19.2C33.4 20 33.4 21.3 32 22.1L27 24.8V24.8Z" fill="url(#paint1_linear_7_408)"/>
<path d="M27.1 24.7L22.9 20.5L10.4 33C10.9 33.5 11.6 33.5 12.5 33.1L27.1 24.7" fill="url(#paint2_linear_7_408)"/>
<path d="M27.1 16.3001L12.5 8.00005C11.6 7.50005 10.9 7.60005 10.4 8.10005L22.9 20.5001L27.1 16.3001V16.3001Z" fill="url(#paint3_linear_7_408)"/>
<path opacity="0.2" d="M27 24.6L12.5 32.8C11.7 33.3 11 33.2 10.5 32.8L10.4 32.9L10.5 33C11 33.4 11.7 33.5 12.5 33L27 24.6Z" fill="black"/>
<path opacity="0.12" d="M10.4 32.8C10.1 32.5 10 32 10 31.4V31.5C10 32.1 10.2 32.6 10.5 32.9V32.8H10.4ZM32 21.8L27 24.6L27.1 24.7L32 21.9C32.7 21.5 33 21 33 20.5C33 21 32.6 21.4 32 21.8V21.8Z" fill="black"/>
<path opacity="0.25" d="M12.5 8.10003L32 19.2C32.6 19.6 33 20 33 20.5C33 20 32.7 19.5 32 19.1L12.5 8.00003C11.1 7.20003 10 7.80003 10 9.40003V9.50003C10 8.00003 11.1 7.30003 12.5 8.10003Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear_7_408" x1="21.8" y1="9.21" x2="5.017" y2="25.992" gradientUnits="userSpaceOnUse">
<stop stop-color="#00A0FF"/>
<stop offset="0.007" stop-color="#00A1FF"/>
<stop offset="0.26" stop-color="#00BEFF"/>
<stop offset="0.512" stop-color="#00D2FF"/>
<stop offset="0.76" stop-color="#00DFFF"/>
<stop offset="1" stop-color="#00E3FF"/>
</linearGradient>
<linearGradient id="paint1_linear_7_408" x1="33.834" y1="20.501" x2="9.63699" y2="20.501" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFE000"/>
<stop offset="0.409" stop-color="#FFBD00"/>
<stop offset="0.775" stop-color="#FFA500"/>
<stop offset="1" stop-color="#FF9C00"/>
</linearGradient>
<linearGradient id="paint2_linear_7_408" x1="24.827" y1="22.796" x2="2.069" y2="45.554" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF3A44"/>
<stop offset="1" stop-color="#C31162"/>
</linearGradient>
<linearGradient id="paint3_linear_7_408" x1="7.29699" y1="0.676051" x2="17.46" y2="10.8391" gradientUnits="userSpaceOnUse">
<stop stop-color="#32A071"/>
<stop offset="0.069" stop-color="#2DA771"/>
<stop offset="0.476" stop-color="#15CF74"/>
<stop offset="0.801" stop-color="#06E775"/>
<stop offset="1" stop-color="#00F076"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

@@ -0,0 +1,10 @@
<svg width="34" height="35" viewBox="0 0 34 35" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.02958 8.60922L8.622 14.2013L14.3705 8.45375L17.1669 11.2498L11.4183 16.9972L17.0114 22.5895L14.1373 25.4633L8.54422 19.871L2.79636 25.6187L0 22.8227L5.74794 17.075L0.155484 11.483L3.02958 8.60922Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0923 25.5156L16.944 22.6642L16.9429 22.6634L22.6467 16.9612L17.0513 11.3675L17.0523 11.367L14.2548 8.56979L8.65972 2.97535L11.5114 0.123963L17.1061 5.71849L22.8099 0.015625L25.6074 2.81285L19.9035 8.51562L25.4984 14.1099L31.2025 8.40729L34 11.2045L28.2958 16.907L33.8917 22.5017L31.0399 25.3531L25.4442 19.7584L19.7409 25.4611L25.3365 31.0559L22.4848 33.9073L16.8892 28.3124L11.1864 34.0156L8.38885 31.2184L14.0923 25.5156Z" fill="url(#paint0_linear_656_10815)"/>
<defs>
<linearGradient id="paint0_linear_656_10815" x1="12.8381" y1="-0.678252" x2="9.54355" y2="31.4493" gradientUnits="userSpaceOnUse">
<stop stop-color="#01F1FF"/>
<stop offset="1" stop-color="#0197FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,15 @@
<svg width="34" height="34" viewBox="0 0 34 34" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_14_10)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.02972 8.59396L8.62219 14.186L14.3703 8.43848L17.1668 11.2346L11.4182 16.982L17.0112 22.5742L14.1371 25.448L8.5441 19.8557L2.79651 25.6035L0 22.8074L5.74813 17.0597L0.155656 11.4678L3.02972 8.59396Z" fill="#023789"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.0922 25.5L16.9434 22.6486L16.9423 22.6478L22.6464 16.9456L17.0512 11.3519L17.0518 11.3514L14.2542 8.55418L8.65961 2.95973L11.5114 0.108337L17.106 5.70288L22.8095 0L25.607 2.79722L19.903 8.5L25.4981 14.0943L31.2022 8.39169L33.9997 11.1889L28.2957 16.8914L33.8914 22.4861L31.0396 25.3375L25.4439 19.7428L19.7404 25.4454L25.3361 31.0403L22.4843 33.8917L16.8887 28.2968L11.1862 34L8.38867 31.2028L14.0922 25.5Z" fill="url(#paint0_linear_14_10)"/>
</g>
<defs>
<linearGradient id="paint0_linear_14_10" x1="12.8379" y1="-0.693875" x2="9.54344" y2="31.4337" gradientUnits="userSpaceOnUse">
<stop stop-color="#01F1FF"/>
<stop offset="1" stop-color="#0197FF"/>
</linearGradient>
<clipPath id="clip0_14_10">
<rect width="34" height="34" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2.92814 12.6789C6.17655 15.9347 11.7044 15.6463 14.5425 12.0117C14.6636 11.8566 14.6825 11.6448 14.5907 11.4708C14.4989 11.2967 14.3136 11.1926 14.1172 11.2049C11.5269 11.3673 8.97627 10.4315 7.0743 8.52765C5.17264 6.62414 4.23958 4.06868 4.40169 1.47281C4.41397 1.2762 4.30965 1.09069 4.13526 0.999048C3.96088 0.907402 3.74893 0.926696 3.59396 1.04833C3.36099 1.23117 3.13828 1.42685 2.92823 1.63726C-0.111372 4.68223 -0.111585 9.63533 2.92814 12.6789Z" stroke="black" stroke-miterlimit="10" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 632 B

File diff suppressed because one or more lines are too long
+39
View File
@@ -0,0 +1,39 @@
const isMobile = {
Android: () => navigator.userAgent.match(/Android/i),
iOS: () => navigator.userAgent.match(/iPhone|iPad|iPod/i)
};
window.addEventListener('click', clickHandler)
if (isMobile.iOS) {
for (const btn of document.getElementsByClassName("close-overlay-btn")) {
btn.addEventListener("touchend", (e) => setTimeout(() => closeOverlay(e), 100))
}
}
function clickHandler(e) {
if (e.target.closest('.contact-tab-btn')) {
e.target.closest('.contact-tab').classList.toggle('active')
}
}
window.addEventListener('load', () => {
const googlePlayBtn = document.querySelector('.google-play-btn');
const appleStoreBtn = document.querySelector('.apple-store-btn');
const fDroidBtn = document.querySelector('.f-droid-btn');
if (!googlePlayBtn || !appleStoreBtn || !fDroidBtn) return;
if (isMobile.Android()) {
googlePlayBtn.classList.remove('hidden');
fDroidBtn.classList.remove('hidden');
}
else if (isMobile.iOS()) {
appleStoreBtn.classList.remove('hidden');
}
else {
appleStoreBtn.classList.remove('hidden');
googlePlayBtn.classList.remove('hidden');
fDroidBtn.classList.remove('hidden');
}
})
+414
View File
@@ -0,0 +1,414 @@
@font-face {
font-family: Gilroy;
src: url("GilroyRegular.woff2") format("woff2");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: Gilroy;
src: url("GilroyLight.woff2") format("woff2");
font-weight: 300;
font-style: normal;
}
@font-face {
font-family: Gilroy;
src: url("GilroyMedium.woff2") format("woff2");
font-weight: 500;
font-style: normal;
}
@font-face {
font-family: Gilroy;
src: url("GilroyBold.woff2") format("woff2");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: Gilroy;
src: url("GilroyRegularItalic.woff2") format("woff2");
font-weight: 400;
font-style: italic;
}
html {
scroll-behavior: smooth;
font-family: Gilroy, Helvetica, sans-serif;
;
letter-spacing: 0.003em;
}
img {
user-select: none;
-webkit-user-select: none;
/* For Safari and older Chrome versions */
-moz-user-select: none;
/* For Firefox */
-ms-user-select: none;
/* For Internet Explorer and Edge */
}
a{
word-wrap: break-word;
}
/* NEW SITE */
.container,
.container-fluid,
.container-xxl,
.container-xl,
.container-lg,
.container-md,
.container-sm {
width: 100%;
/* padding: 0 20px; */
margin-right: auto;
margin-left: auto;
}
@media (min-width: 576px) {
.container-sm,
.container {
max-width: 540px;
}
}
@media (min-width: 768px) {
.container-md,
.container-sm,
.container {
max-width: 720px;
}
}
@media (min-width: 992px) {
.container-lg,
.container-md,
.container-sm,
.container {
max-width: 960px;
}
}
@media (min-width: 1200px) {
.container-xl,
.container-lg,
.container-md,
.container-sm,
.container {
max-width: 1140px;
}
}
@media (min-width: 1400px) {
.container-xxl,
.container-xl,
.container-lg,
.container-md,
.container-sm,
.container {
max-width: 1320px;
}
}
.gradient-text {
background: -webkit-linear-gradient(to bottom, #53C1FF -50%, #0053D0 160%);
background: linear-gradient(to bottom, #53C1FF -50%, #0053D0 160%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-fill-color: transparent;
}
.dark .border-gradient {
background:
linear-gradient(#11182F, #11182F) padding-box,
linear-gradient(to bottom, transparent, #01F1FF 58%) border-box;
border: 1px solid transparent;
}
.dark .only-light {
display: none;
}
.only-dark {
display: none;
}
.dark .only-dark {
display: inherit;
}
.menu-link {
font-size: 16px;
line-height: 33.42px;
color: #0D0E12;
}
.dark .menu-link {
color: #fff;
}
.nav-link ul li a.active {
color: #0053D0;
}
.dark .nav-link ul li a.active {
color: #66D9E2;
}
@media (min-width:1024px) {
.nav-link-text,
.menu-link {
display: inline-block;
position: relative;
color: #0D0E12;
}
.nav-link-text::before,
.active .nav-link-text::before,
.menu-link::before {
content: "";
position: absolute;
width: 0;
height: 1px;
bottom: 0;
right: 0;
/* background-color: initial; */
transition: width 0.25s ease-out;
}
.menu-link::before {
background-color: #0D0E12;
}
.dark .menu-link::before {
background-color: #fff;
}
.active .nav-link-text::before {
width: 100%;
}
.nav-link:hover .nav-link-text::before,
.menu-link:hover::before {
width: 100%;
left: 0;
right: auto;
}
}
.sub-menu {
visibility: hidden;
opacity: 0;
color: #505158;
}
.sub-menu .no-hover {
color: #505158 !important;
}
.dark .sub-menu,
.dark .sub-menu .no-hover {
color: #fff !important;
}
.dark .sub-menu li:hover {
color: #66D9E2;
}
.sub-menu li:hover {
color: #0053D0;
}
.sub-menu {
transition: all .3s ease !important;
}
.nav-link span svg,
header nav {
transition: all 0.5s ease;
}
.nav-link:hover span svg {
transform: rotate(180deg);
}
@media (min-width:1024px) {
.nav-link:hover .sub-menu,
.nav-link:focus-within .sub-menu {
visibility: visible;
opacity: 1;
margin-top: 0;
}
}
@media (max-width: 1024px) {
.sub-menu {
max-height: 0;
transform: translateY(-10px);
transition: all .7s ease !important;
}
.active .sub-menu {
max-height: 600px;
transform: translateY(0px);
opacity: 1;
visibility: visible;
margin-top: 0;
}
header nav {
visibility: hidden;
opacity: 0;
transform: translateX(100%);
}
header nav.open {
visibility: visible;
opacity: 1;
transform: translateX(0);
}
}
.lock-scroll {
overflow: hidden;
}
/* hero */
header {
transition: all .7s ease;
}
.primary-header {
background: linear-gradient(270deg, #0053D0 35.85%, #0197FF 94.78%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0px 4px 74px #e9e7e2;
}
.dark .primary-header {
background: linear-gradient(270deg, #70F0F9 100%, #70F0F9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
}
.secondary-header {
color: #606c71;
text-shadow: 0px 4px 74px #e9e7e2;
}
.dark .secondary-header {
color: #fff;
text-shadow: none;
}
.description {
width: 31rem;
}
p a {
color: #0053D0;
text-decoration: underline;
text-underline-offset: 2px;
}
.dark p a {
color: #70F0F9;
}
/* For Contact & Invitation Page */
.primary-header-contact {
background: linear-gradient(251.16deg, #53c1ff 1.1%, #0053d0 100.82%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: 0px 4px 74px #e9e7e2;
}
.dark .primary-header-contact {
background: linear-gradient(270deg, #70F0F9 100%, #70F0F9 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
text-shadow: none;
}
.secondary-header-contact {
text-shadow: 0px 4px 74px #e9e7e2;
}
.dark .secondary-header-contact {
text-shadow: none;
}
.content_copy_with_tooltip {
background-color: #f8f8f6;
border-radius: 50px;
padding-bottom: 4px;
padding-top: 8px;
margin-top: 16px;
margin-bottom: 16px;
}
.content_copy_with_tooltip .tooltip {
vertical-align: -6px;
}
.content_copy_with_tooltip .content {
font-size: 15px;
}
.contact-tab>.contact-tab-content,
.job-tab>.job-tab-content {
opacity: 0;
max-height: 0;
transition: all 0.5s ease;
visibility: hidden;
transform: translateY(10px);
overflow: hidden;
}
.contact-tab svg,
.job-tab svg {
transform: rotate(-180deg);
transition: all .5s ease;
}
.contact-tab.active>.contact-tab-content,
.job-tab.active>.job-tab-content {
opacity: 1;
max-height: 300px;
visibility: visible;
transform: translateY(0px);
}
.for-tablet .contact-tab.active>.contact-tab-content,
.for-tablet .job-tab.active>.job-tab-content {
min-height: 450px;
}
.contact-tab.active svg,
.contact-tab:hover svg,
.job-tab.active svg,
.job-tab:hover svg {
transform: rotate(0deg);
}
.d-none-if-js-disabled {
display: none !important;
}
+11
View File
@@ -0,0 +1,11 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M23.9958 12.2815C23.9363 12.3954 23.8849 12.5146 23.8158 12.6224C23.5945 12.9674 23.2766 13.1624 22.8654 13.1685C22.2182 13.1782 21.5707 13.1764 20.9234 13.17C20.2781 13.1636 19.7772 12.6464 19.7773 11.9999C19.7774 11.3535 20.2754 10.8389 20.9237 10.8303C21.532 10.8221 22.1405 10.8258 22.7488 10.8278C23.3522 10.8298 23.7281 11.0908 23.9462 11.6508C23.956 11.6761 23.9789 11.6964 23.9958 11.719C23.9958 11.9064 23.9958 12.094 23.9958 12.2815Z" fill="white"/>
<path d="M11.7154 24.0003C11.6217 23.9526 11.5256 23.9088 11.4345 23.8564C11.0545 23.6377 10.836 23.3104 10.8286 22.87C10.8175 22.2149 10.8179 21.5593 10.828 20.9042C10.8378 20.2738 11.3597 19.7812 11.9967 19.7812C12.6336 19.7812 13.155 20.2739 13.1654 20.9042C13.1757 21.5359 13.1717 22.168 13.1682 22.7998C13.1652 23.3392 12.8885 23.7369 12.3906 23.937C12.3509 23.9529 12.3153 23.979 12.2779 24.0003C12.0904 24.0003 11.9029 24.0003 11.7154 24.0003Z" fill="white"/>
<path d="M17.2592 11.9958C17.2733 14.8825 14.9232 17.2468 12.0032 17.2612C9.11732 17.2754 6.75397 14.9264 6.73836 12.0041C6.72295 9.12027 9.07502 6.75326 11.9946 6.73835C14.8788 6.72363 17.2449 9.07587 17.2592 11.9958Z" stroke="white" stroke-width="1.5"/>
<path d="M13.1693 2.11324C13.1692 2.43329 13.1744 2.75345 13.1682 3.07341C13.1555 3.7216 12.6425 4.21934 11.995 4.21864C11.3493 4.21789 10.8358 3.71808 10.828 3.06768C10.8204 2.42766 10.8201 1.78736 10.8283 1.14738C10.8365 0.500704 11.3562 -0.000843934 12.0007 1.06615e-06C12.6437 0.000846066 13.1564 0.504314 13.1684 1.15307C13.1743 1.47303 13.1694 1.79318 13.1693 2.11324Z" fill="white"/>
<path d="M2.10878 13.1714C1.78877 13.1714 1.46872 13.1754 1.14885 13.1705C0.504832 13.1605 -0.000422735 12.6426 2.65407e-07 11.9987C0.000423265 11.3553 0.503376 10.838 1.15138 10.8301C1.79126 10.8223 2.43138 10.8222 3.07126 10.8303C3.72034 10.8385 4.21822 11.3541 4.21794 12.0012C4.21766 12.6477 3.71555 13.1609 3.06872 13.1706C2.7488 13.1753 2.42875 13.1714 2.10878 13.1714Z" fill="white"/>
<path d="M6.85268 5.524C6.82732 6.152 6.60944 6.52005 6.16969 6.72981C5.73844 6.93552 5.29738 6.90378 4.94534 6.58208C4.41604 6.09838 3.90431 5.59148 3.42451 5.05894C3.02923 4.62023 3.09727 3.9209 3.51626 3.50792C3.9361 3.09409 4.63284 3.03567 5.06893 3.43194C5.59381 3.90888 6.09569 4.41446 6.57188 4.93996C6.73726 5.12252 6.79642 5.4014 6.85268 5.524Z" fill="white"/>
<path d="M17.1426 18.4446C17.1749 17.8424 17.389 17.4819 17.8198 17.2738C18.2418 17.07 18.6812 17.0888 19.0265 17.3998C19.5706 17.8899 20.0919 18.4099 20.5814 18.9544C20.9675 19.384 20.895 20.0764 20.485 20.4873C20.0824 20.8907 19.3961 20.9756 18.9718 20.5988C18.4129 20.1026 17.89 19.5625 17.3864 19.0096C17.2323 18.8405 17.1926 18.5671 17.1426 18.4446Z" fill="white"/>
<path d="M18.2026 6.84235C17.8449 6.82333 17.4821 6.61377 17.2723 6.18256C17.0626 5.7515 17.0878 5.30837 17.4061 4.95629C17.8919 4.41887 18.4055 3.90234 18.945 3.41897C19.3826 3.02693 20.0905 3.1037 20.4947 3.52429C20.9057 3.95198 20.9561 4.64003 20.5568 5.07805C20.0843 5.59645 19.576 6.08306 19.071 6.5708C18.8688 6.76619 18.6057 6.84832 18.2026 6.84235Z" fill="white"/>
<path d="M5.54205 17.1445C6.14812 17.1747 6.51058 17.385 6.72137 17.8153C6.9323 18.2459 6.90765 18.6892 6.58942 19.0415C6.1037 19.5791 5.58933 20.0948 5.05088 20.5795C4.62165 20.9659 3.93035 20.8989 3.51681 20.4933C3.09875 20.0833 3.02864 19.3789 3.4227 18.942C3.908 18.404 4.42706 17.8934 4.96382 17.4066C5.13982 17.247 5.41663 17.1985 5.54205 17.1445Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

+20
View File
@@ -0,0 +1,20 @@
module Main where
import Control.Logger.Simple
import Simplex.Messaging.Server.CLI (getEnvPath)
import Simplex.Messaging.Notifications.Server.Main
defaultCfgPath :: FilePath
defaultCfgPath = "/etc/opt/simplex-notifications"
defaultLogPath :: FilePath
defaultLogPath = "/var/opt/simplex-notifications"
logCfg :: LogConfig
logCfg = LogConfig {lc_file = Nothing, lc_stderr = True}
main :: IO ()
main = do
cfgPath <- getEnvPath "NTF_SERVER_CFG_PATH" defaultCfgPath
logPath <- getEnvPath "NTF_SERVER_LOG_PATH" defaultLogPath
withGlobalLogging logCfg $ ntfServerCLI cfgPath logPath
+46
View File
@@ -0,0 +1,46 @@
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Main where
import Control.Logger.Simple
import Data.ByteArray (ScrubbedBytes)
import qualified Data.List.NonEmpty as L
import qualified Data.Map.Strict as M
import Simplex.Messaging.Agent.Env.SQLite
import Simplex.Messaging.Agent.Server (runSMPAgent)
import Simplex.Messaging.Agent.Store.SQLite (MigrationConfirmation (..))
import Simplex.Messaging.Client (defaultNetworkConfig)
import Simplex.Messaging.Transport (TLS, Transport (..))
cfg :: AgentConfig
cfg = defaultAgentConfig
agentDbFile :: String
agentDbFile = "smp-agent.db"
agentDbKey :: ScrubbedBytes
agentDbKey = ""
servers :: InitialAgentServers
servers =
InitialAgentServers
{ smp = M.fromList [(1, L.fromList ["smp://bU0K-bRg24xWW__lS0umO1Zdw_SXqpJNtm1_RrPLViE=@localhost:5223"])],
ntf = [],
xftp = M.empty,
netCfg = defaultNetworkConfig
}
logCfg :: LogConfig
logCfg = LogConfig {lc_file = Nothing, lc_stderr = True}
-- Warning: this SMP agent server is experimental - it does not work correctly with multiple connected TCP clients in some cases.
main :: IO ()
main = do
let AgentConfig {tcpPort} = cfg
putStrLn $ maybe (error "no agent port") (\port -> "SMP agent listening on port " ++ port) tcpPort
setLogLevel LogInfo -- LogError
Right st <- createAgentStore agentDbFile agentDbKey False MCConsole
withGlobalLogging logCfg $ runSMPAgent (transport @TLS) cfg servers st
+16 -12
View File
@@ -1,18 +1,22 @@
module Main where
import Env.STM
import Server (runSMPServer)
import Control.Logger.Simple
import Simplex.Messaging.Server.CLI (getEnvPath)
import Simplex.Messaging.Server.Main (smpServerCLI_)
import Simplex.Messaging.Server.Web (serveStaticFiles, attachStaticFiles)
import SMPWeb (smpGenerateSite)
cfg :: Config
cfg =
Config
{ tcpPort = "5223",
tbqSize = 16,
queueIdBytes = 12,
msgIdBytes = 6
}
defaultCfgPath :: FilePath
defaultCfgPath = "/etc/opt/simplex"
defaultLogPath :: FilePath
defaultLogPath = "/var/opt/simplex"
logCfg :: LogConfig
logCfg = LogConfig {lc_file = Nothing, lc_stderr = True}
main :: IO ()
main = do
putStrLn $ "Listening on port " ++ tcpPort cfg
runSMPServer cfg
cfgPath <- getEnvPath "SMP_SERVER_CFG_PATH" defaultCfgPath
logPath <- getEnvPath "SMP_SERVER_LOG_PATH" defaultLogPath
withGlobalLogging logCfg $ smpServerCLI_ smpGenerateSite serveStaticFiles attachStaticFiles cfgPath logPath
+43
View File
@@ -0,0 +1,43 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module SMPWeb
( smpGenerateSite,
serverInformation,
) where
import Data.ByteString (ByteString)
import Data.String (fromString)
import Web.Embedded (embeddedContent)
import Simplex.Messaging.Encoding.String (strEncode)
import Simplex.Messaging.Server.Information
import Simplex.Messaging.Server.Main (simplexmqSource)
import qualified Simplex.Messaging.Server.Web as Web
import Simplex.Messaging.Server.Web (render, serverInfoSubsts, timedTTLText)
import Simplex.Messaging.Transport.Client (TransportHost (..))
smpGenerateSite :: ServerInformation -> Maybe TransportHost -> FilePath -> IO ()
smpGenerateSite si onionHost path =
Web.generateSite embeddedContent (serverInformation si onionHost) smpLinkPages path
smpLinkPages :: [String]
smpLinkPages = ["contact", "invitation", "a", "c", "g", "r", "i"]
serverInformation :: ServerInformation -> Maybe TransportHost -> ByteString
serverInformation ServerInformation {config, information} onionHost = render (Web.indexHtml embeddedContent) substs
where
substs = [("smpConfig", Just "y"), ("xftpConfig", Nothing)] <> substConfig <> serverInfoSubsts simplexmqSource information <> [("onionHost", strEncode <$> onionHost), ("iniFileName", Just "smp-server.ini")]
substConfig =
[ ( "persistence",
Just $ case persistence config of
SPMMemoryOnly -> "In-memory only"
SPMQueues -> "Queues"
SPMMessages -> "Queues and messages"
),
("messageExpiration", Just $ maybe "Never" (fromString . timedTTLText) $ messageExpiration config),
("statsEnabled", Just . yesNo $ statsEnabled config),
("newQueuesAllowed", Just . yesNo $ newQueuesAllowed config),
("basicAuthEnabled", Just . yesNo $ basicAuthEnabled config)
]
yesNo True = "Yes"
yesNo False = "No"
+23
View File
@@ -0,0 +1,23 @@
module Main where
import Control.Logger.Simple
import Simplex.FileTransfer.Server.Main (xftpServerCLI_)
import Simplex.Messaging.Server.CLI (getEnvPath)
import Simplex.Messaging.Server.Web (serveStaticFiles)
import XFTPWeb (xftpGenerateSite)
defaultCfgPath :: FilePath
defaultCfgPath = "/etc/opt/simplex-xftp"
defaultLogPath :: FilePath
defaultLogPath = "/var/opt/simplex-xftp"
logCfg :: LogConfig
logCfg = LogConfig {lc_file = Nothing, lc_stderr = True}
main :: IO ()
main = do
setLogLevel LogDebug -- change to LogError in production
cfgPath <- getEnvPath "XFTP_SERVER_CFG_PATH" defaultCfgPath
logPath <- getEnvPath "XFTP_SERVER_LOG_PATH" defaultLogPath
withGlobalLogging logCfg $ xftpServerCLI_ xftpGenerateSite serveStaticFiles cfgPath logPath
+67
View File
@@ -0,0 +1,67 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module XFTPWeb
( xftpGenerateSite,
xftpServerInformation,
) where
import Control.Monad (forM_)
import qualified Data.ByteString.Char8 as B
import Data.ByteString (ByteString)
import Data.FileEmbed (embedDir, embedFile)
import Data.Maybe (isJust)
import Data.String (fromString)
import Web.Embedded (embeddedContent)
import Simplex.FileTransfer.Server.Env (XFTPServerConfig (..))
import Simplex.Messaging.Encoding.String (strEncode)
import Simplex.Messaging.Server.Expiration (ExpirationConfig (..))
import Simplex.Messaging.Server.Information (ServerPublicInfo)
import Simplex.Messaging.Server.Main (simplexmqSource)
import qualified Simplex.Messaging.Server.Web as Web
import Simplex.Messaging.Server.Web (render, serverInfoSubsts, timedTTLText)
import Simplex.Messaging.Transport.Client (TransportHost (..))
import System.Directory (createDirectoryIfMissing)
import System.FilePath ((</>))
xftpWebContent :: [(FilePath, ByteString)]
xftpWebContent = $(embedDir "apps/xftp-server/static/xftp-web-bundle/")
xftpMediaContent :: [(FilePath, ByteString)]
xftpMediaContent = $(embedDir "apps/xftp-server/static/media/")
xftpFilePageHtml :: ByteString
xftpFilePageHtml = $(embedFile "apps/xftp-server/static/file.html")
xftpGenerateSite :: XFTPServerConfig s -> Maybe ServerPublicInfo -> Maybe TransportHost -> FilePath -> IO ()
xftpGenerateSite cfg info onionHost path = do
let substs = xftpSubsts cfg info onionHost
Web.generateSite embeddedContent (render (Web.indexHtml embeddedContent) substs) [] path
let xftpDir = path </> "xftp-web-bundle"
mediaDir = path </> "media"
fileDir = path </> "file"
filePage xftpDir xftpWebContent
filePage mediaDir xftpMediaContent
createDirectoryIfMissing True fileDir
B.writeFile (fileDir </> "index.html") $ render xftpFilePageHtml substs
where
filePage dir content_ = do
createDirectoryIfMissing True dir
forM_ content_ $ \(fp, content) -> B.writeFile (dir </> fp) content
xftpServerInformation :: XFTPServerConfig s -> Maybe ServerPublicInfo -> Maybe TransportHost -> ByteString
xftpServerInformation cfg info onionHost = render (Web.indexHtml embeddedContent) (xftpSubsts cfg info onionHost)
xftpSubsts :: XFTPServerConfig s -> Maybe ServerPublicInfo -> Maybe TransportHost -> [(ByteString, Maybe ByteString)]
xftpSubsts XFTPServerConfig {fileExpiration, logStatsInterval, allowNewFiles, newFileBasicAuth} information onionHost =
[("smpConfig", Nothing), ("xftpConfig", Just "y")] <> substConfig <> serverInfoSubsts simplexmqSource information <> [("onionHost", strEncode <$> onionHost), ("iniFileName", Just "file-server.ini")]
where
substConfig =
[ ("fileExpiration", Just $ maybe "Never" (fromString . timedTTLText . ttl) fileExpiration),
("statsEnabled", Just . yesNo $ isJust logStatsInterval),
("newUploadsAllowed", Just . yesNo $ allowNewFiles),
("basicAuthEnabled", Just . yesNo $ isJust newFileBasicAuth)
]
yesNo True = "Yes"
yesNo False = "No"
File diff suppressed because one or more lines are too long
@@ -0,0 +1,115 @@
<svg width="440" height="520" viewBox="-20 0 440 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Sender browser -->
<rect x="120" y="16" width="160" height="56" rx="10" stroke="#70F0F9" stroke-width="1.5"/>
<text x="200" y="40" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#70F0F9">Sender's browser</text>
<text x="200" y="56" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="rgba(112,240,249,0.7)">encrypts file</text>
<!-- Arrow down from sender to chunks -->
<line x1="200" y1="72" x2="200" y2="120" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<!-- Chunks row -->
<rect x="112" y="120" width="176" height="40" rx="8" fill="none" stroke="#70F0F9" stroke-width="1" stroke-dasharray="4 3"/>
<text x="200" y="145" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" fill="#70F0F9">encrypted chunks</text>
<!-- Arrows from chunks to routers -->
<line x1="152" y1="160" x2="80" y2="220" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<line x1="200" y1="160" x2="200" y2="220" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<line x1="248" y1="160" x2="320" y2="220" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<!-- Router 1 (SimpleX) -->
<rect x="20" y="220" width="120" height="56" rx="6" fill="none" stroke="#70F0F9" stroke-width="1.5"/>
<g transform="translate(28, 227)">
<rect width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="6" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="12" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<circle cx="11" cy="2" r="1" fill="#70F0F9"/>
<circle cx="11" cy="8" r="1" fill="#70F0F9"/>
<circle cx="11" cy="14" r="1" fill="#70F0F9"/>
</g>
<text x="80" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#70F0F9">SimpleX</text>
<text x="80" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="rgba(112,240,249,0.7)">XFTP router</text>
<!-- Router 2 (Flux) -->
<rect x="155" y="220" width="90" height="56" rx="6" fill="none" stroke="#70F0F9" stroke-width="1.5"/>
<g transform="translate(163, 227)">
<rect width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="6" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="12" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<circle cx="11" cy="2" r="1" fill="#70F0F9"/>
<circle cx="11" cy="8" r="1" fill="#70F0F9"/>
<circle cx="11" cy="14" r="1" fill="#70F0F9"/>
</g>
<text x="200" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#70F0F9">Flux</text>
<text x="200" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="rgba(112,240,249,0.7)">XFTP router</text>
<!-- Router 3 (SimpleX) -->
<rect x="260" y="220" width="120" height="56" rx="6" fill="none" stroke="#70F0F9" stroke-width="1.5"/>
<g transform="translate(268, 227)">
<rect width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="6" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<rect y="12" width="14" height="4" rx="1" fill="rgba(112,240,249,0.5)"/>
<circle cx="11" cy="2" r="1" fill="#70F0F9"/>
<circle cx="11" cy="8" r="1" fill="#70F0F9"/>
<circle cx="11" cy="14" r="1" fill="#70F0F9"/>
</g>
<text x="320" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#70F0F9">SimpleX</text>
<text x="320" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="rgba(112,240,249,0.7)">XFTP router</text>
<!-- Arrows from routers down -->
<line x1="80" y1="276" x2="152" y2="336" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<line x1="200" y1="276" x2="200" y2="336" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<line x1="320" y1="276" x2="248" y2="336" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<!-- Re-encrypt label -->
<text x="330" y="310" text-anchor="start" font-family="system-ui, sans-serif" font-size="10" fill="rgba(112,240,249,0.7)">re-encrypted</text>
<text x="330" y="322" text-anchor="start" font-family="system-ui, sans-serif" font-size="10" fill="rgba(112,240,249,0.7)">per recipient</text>
<!-- Chunks row (download) -->
<rect x="112" y="336" width="176" height="40" rx="8" fill="none" stroke="#70F0F9" stroke-width="1" stroke-dasharray="4 3"/>
<text x="200" y="361" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" fill="#70F0F9">encrypted chunks</text>
<!-- Arrow down to recipient -->
<line x1="200" y1="376" x2="200" y2="424" stroke="#70F0F9" stroke-width="1.5" marker-end="url(#arrowC)"/>
<!-- Recipient browser -->
<rect x="120" y="424" width="160" height="56" rx="10" stroke="#70F0F9" stroke-width="1.5"/>
<text x="200" y="448" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#70F0F9">Recipient's browser</text>
<text x="200" y="464" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="rgba(112,240,249,0.7)">decrypts file</text>
<!-- Key path (dashed, side) -->
<path d="M120 44 L8 44 L8 452 L120 452" stroke="#70F0F9" stroke-width="1.5" stroke-dasharray="6 4" fill="none" marker-end="url(#arrowC)"/>
<text x="-6" y="240" text-anchor="middle" font-family="system-ui, sans-serif" font-size="10" fill="#70F0F9" transform="rotate(-90 -6 240)">key in URL fragment - never sent to page server or data router</text>
<!-- Closed padlock: encryption (between sender and chunks) -->
<g transform="translate(192, 88)">
<path d="M4,7 V4 C4,1.2 12,1.2 12,4 V7" stroke="#60a5fa" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<rect x="2" y="7" width="12" height="9" rx="2" fill="#60a5fa"/>
<circle cx="8" cy="12" r="1.2" fill="#0B2A59"/>
</g>
<!-- Open padlock: decryption (between chunks and recipient) -->
<g transform="translate(192, 392)">
<path d="M4,7 V4 C4,1.2 12,1.2 12,4 V2" stroke="#60a5fa" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<rect x="2" y="7" width="12" height="9" rx="2" fill="#60a5fa"/>
<circle cx="8" cy="12" r="1.2" fill="#0B2A59"/>
</g>
<!-- Key icon on dashed line -->
<g transform="translate(8, 410)">
<circle cx="0" cy="0" r="6" stroke="#FBBF24" stroke-width="2" fill="#FBBF24"/>
<circle cx="0" cy="0" r="2" fill="#0B2A59"/>
<line x1="6" y1="0" x2="16" y2="0" stroke="#FBBF24" stroke-width="2"/>
<line x1="14" y1="0" x2="14" y2="4" stroke="#FBBF24" stroke-width="2"/>
<line x1="11" y1="0" x2="11" y2="3.5" stroke="#FBBF24" stroke-width="2"/>
</g>
<!-- Annotation: no shared IDs -->
<text x="200" y="510" text-anchor="middle" font-family="system-ui, sans-serif" font-size="10" fill="rgba(112,240,249,0.7)">Each file fragment uses unique anonymous credentials - no shared identifiers</text>
<defs>
<marker id="arrowC" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#70F0F9"/>
</marker>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.2 KiB

@@ -0,0 +1,130 @@
<svg width="440" height="520" viewBox="-20 0 440 520" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Sender browser -->
<rect x="120" y="16" width="160" height="56" rx="10" fill="url(#gBox)" stroke="#606C71" stroke-width="1.5"/>
<text x="200" y="40" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#fff">Sender's browser</text>
<text x="200" y="56" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="rgba(255,255,255,0.8)">encrypts file</text>
<!-- Arrow down from sender to chunks -->
<line x1="200" y1="72" x2="200" y2="120" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<!-- Chunks row -->
<rect x="112" y="120" width="176" height="40" rx="8" fill="#f0f7ff" stroke="#0053D0" stroke-width="1" stroke-dasharray="4 3"/>
<text x="200" y="145" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" fill="#0053D0">encrypted chunks</text>
<!-- Arrows from chunks to routers -->
<line x1="152" y1="160" x2="80" y2="220" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<line x1="200" y1="160" x2="200" y2="220" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<line x1="248" y1="160" x2="320" y2="220" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<!-- Router 1 (SimpleX) -->
<rect x="20" y="220" width="120" height="56" rx="6" fill="#f0f4f8" stroke="#606C71" stroke-width="1.5"/>
<g transform="translate(28, 227)">
<rect width="14" height="4" rx="1" fill="#606C71"/>
<rect y="6" width="14" height="4" rx="1" fill="#606C71"/>
<rect y="12" width="14" height="4" rx="1" fill="#606C71"/>
<circle cx="11" cy="2" r="1" fill="#53C1FF"/>
<circle cx="11" cy="8" r="1" fill="#53C1FF"/>
<circle cx="11" cy="14" r="1" fill="#53C1FF"/>
</g>
<text x="80" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#3F484B">SimpleX</text>
<text x="80" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#606C71">XFTP router</text>
<!-- Router 2 (Flux) -->
<rect x="155" y="220" width="90" height="56" rx="6" fill="#f0f4f8" stroke="#606C71" stroke-width="1.5"/>
<g transform="translate(163, 227)">
<rect width="14" height="4" rx="1" fill="#606C71"/>
<rect y="6" width="14" height="4" rx="1" fill="#606C71"/>
<rect y="12" width="14" height="4" rx="1" fill="#606C71"/>
<circle cx="11" cy="2" r="1" fill="#53C1FF"/>
<circle cx="11" cy="8" r="1" fill="#53C1FF"/>
<circle cx="11" cy="14" r="1" fill="#53C1FF"/>
</g>
<text x="200" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#3F484B">Flux</text>
<text x="200" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#606C71">XFTP router</text>
<!-- Router 3 (SimpleX) -->
<rect x="260" y="220" width="120" height="56" rx="6" fill="#f0f4f8" stroke="#606C71" stroke-width="1.5"/>
<g transform="translate(268, 227)">
<rect width="14" height="4" rx="1" fill="#606C71"/>
<rect y="6" width="14" height="4" rx="1" fill="#606C71"/>
<rect y="12" width="14" height="4" rx="1" fill="#606C71"/>
<circle cx="11" cy="2" r="1" fill="#53C1FF"/>
<circle cx="11" cy="8" r="1" fill="#53C1FF"/>
<circle cx="11" cy="14" r="1" fill="#53C1FF"/>
</g>
<text x="320" y="244" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" font-weight="600" fill="#3F484B">SimpleX</text>
<text x="320" y="258" text-anchor="middle" font-family="system-ui, sans-serif" font-size="9" fill="#606C71">XFTP router</text>
<!-- Arrows from routers down -->
<line x1="80" y1="276" x2="152" y2="336" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<line x1="200" y1="276" x2="200" y2="336" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<line x1="320" y1="276" x2="248" y2="336" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<!-- Re-encrypt label -->
<text x="330" y="310" text-anchor="start" font-family="system-ui, sans-serif" font-size="10" fill="#606C71">re-encrypted</text>
<text x="330" y="322" text-anchor="start" font-family="system-ui, sans-serif" font-size="10" fill="#606C71">per recipient</text>
<!-- Chunks row (download) -->
<rect x="112" y="336" width="176" height="40" rx="8" fill="#f0f7ff" stroke="#0053D0" stroke-width="1" stroke-dasharray="4 3"/>
<text x="200" y="361" text-anchor="middle" font-family="system-ui, sans-serif" font-size="12" fill="#0053D0">encrypted chunks</text>
<!-- Arrow down to recipient -->
<line x1="200" y1="376" x2="200" y2="424" stroke="#606C71" stroke-width="1.5" marker-end="url(#arrowG)"/>
<!-- Recipient browser -->
<rect x="120" y="424" width="160" height="56" rx="10" fill="url(#gBox)" stroke="#606C71" stroke-width="1.5"/>
<text x="200" y="448" text-anchor="middle" font-family="system-ui, sans-serif" font-size="13" font-weight="600" fill="#fff">Recipient's browser</text>
<text x="200" y="464" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="rgba(255,255,255,0.8)">decrypts file</text>
<!-- Key path (dashed, side) -->
<path d="M120 44 L8 44 L8 452 L120 452" stroke="#0053D0" stroke-width="1.5" stroke-dasharray="6 4" fill="none" marker-end="url(#arrowB)"/>
<text x="-6" y="240" text-anchor="middle" font-family="system-ui, sans-serif" font-size="10" fill="#0053D0" transform="rotate(-90 -6 240)">key in URL fragment - never sent to page server or data router</text>
<!-- Closed padlock: encryption (between sender and chunks) -->
<g transform="translate(192, 88)">
<path d="M4,7 V4 C4,1.2 12,1.2 12,4 V7" stroke="#0053D0" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<rect x="2" y="7" width="12" height="9" rx="2" fill="#0053D0"/>
<circle cx="8" cy="12" r="1.2" fill="#fff"/>
</g>
<!-- Open padlock: decryption (between chunks and recipient) -->
<g transform="translate(192, 392)">
<path d="M4,7 V4 C4,1.2 12,1.2 12,4 V2" stroke="#0053D0" stroke-width="1.5" fill="none" stroke-linecap="round"/>
<rect x="2" y="7" width="12" height="9" rx="2" fill="#0053D0"/>
<circle cx="8" cy="12" r="1.2" fill="#fff"/>
</g>
<!-- Key icon on dashed line -->
<g transform="translate(8, 410)">
<circle cx="0" cy="0" r="6" stroke="#D97706" stroke-width="2" fill="#D97706"/>
<circle cx="0" cy="0" r="2" fill="#fff"/>
<line x1="6" y1="0" x2="16" y2="0" stroke="#D97706" stroke-width="2"/>
<line x1="14" y1="0" x2="14" y2="4" stroke="#D97706" stroke-width="2"/>
<line x1="11" y1="0" x2="11" y2="3.5" stroke="#D97706" stroke-width="2"/>
</g>
<!-- Annotation: no shared IDs -->
<text x="200" y="510" text-anchor="middle" font-family="system-ui, sans-serif" font-size="10" fill="#606C71">Each file fragment uses unique anonymous credentials - no shared identifiers</text>
<defs>
<linearGradient id="gBox" x1="120" y1="16" x2="280" y2="72" gradientUnits="userSpaceOnUse">
<stop stop-color="#0053D0"/>
<stop offset="1" stop-color="#53C1FF"/>
</linearGradient>
<linearGradient id="gSrv1" x1="20" y1="220" x2="140" y2="276" gradientUnits="userSpaceOnUse">
<stop stop-color="#0053D0"/>
<stop offset="1" stop-color="#53C1FF"/>
</linearGradient>
<linearGradient id="gSrv2" x1="155" y1="220" x2="245" y2="276" gradientUnits="userSpaceOnUse">
<stop stop-color="#0053D0"/>
<stop offset="1" stop-color="#53C1FF"/>
</linearGradient>
<marker id="arrowG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#606C71"/>
</marker>
<marker id="arrowB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#0053D0"/>
</marker>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.8 KiB

File diff suppressed because one or more lines are too long
@@ -0,0 +1,145 @@
#app, [data-xftp-app] {
font-family: system-ui, -apple-system, sans-serif;
color: #333;
width: 100%;
max-width: 480px;
padding: 16px;
box-sizing: border-box;
--xftp-ring-fg: #3b82f6;
}
:is(#app, [data-xftp-app]) .card {
background: #fff;
border-radius: 12px;
padding: 32px 24px;
box-shadow: 0 1px 3px rgba(0,0,0,.1);
text-align: center;
}
:is(#app, [data-xftp-app]) h1 {
font-size: 1.25rem;
font-weight: 600;
margin-bottom: 24px;
}
:is(#app, [data-xftp-app]) .stage { margin-top: 16px; }
/* Drop zone */
:is(#app, [data-xftp-app]) .drop-zone {
border: 2px dashed #ccc;
border-radius: 8px;
padding: 32px 16px;
transition: border-color .15s, background .15s;
}
:is(#app, [data-xftp-app]) .drop-zone.drag-over {
border-color: #3b82f6;
background: #eff6ff;
}
/* Buttons */
:is(#app, [data-xftp-app]) .btn {
display: inline-block;
padding: 10px 24px;
border: none;
border-radius: 6px;
background: #3b82f6;
color: #fff;
font-size: .9rem;
font-weight: 500;
cursor: pointer;
transition: background .15s;
}
:is(#app, [data-xftp-app]) .btn:hover { background: #2563eb; }
:is(#app, [data-xftp-app]) .btn-secondary { background: #6b7280; }
:is(#app, [data-xftp-app]) .btn-secondary:hover { background: #4b5563; }
/* Hints */
:is(#app, [data-xftp-app]) .hint { color: #999; font-size: .85rem; margin-top: 8px; }
:is(#app, [data-xftp-app]) .expiry { margin-top: 12px; }
/* Progress */
:is(#app, [data-xftp-app]) .progress-ring { display: block; margin: 0 auto 12px; }
:is(#app, [data-xftp-app]) #upload-status,
:is(#app, [data-xftp-app]) #dl-status { font-size: .9rem; color: #666; margin-bottom: 12px; }
/* Share link row */
:is(#app, [data-xftp-app]) .link-row {
display: flex;
gap: 8px;
margin-top: 12px;
}
:is(#app, [data-xftp-app]) .link-row input {
flex: 1;
padding: 8px 10px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: .85rem;
background: #f9fafb;
}
/* Upload link */
:is(#app, [data-xftp-app]) .upload-link {
margin-top: 12px;
color: #3b82f6;
font-size: .9rem;
text-decoration: none;
cursor: pointer;
}
:is(#app, [data-xftp-app]) .upload-link:not([hidden]) {
display: inline-block;
}
:is(#app, [data-xftp-app]) .upload-link:hover { text-decoration: underline; }
/* Messages */
:is(#app, [data-xftp-app]) .success { color: #16a34a; font-weight: 600; }
:is(#app, [data-xftp-app]) .error { color: #dc2626; font-weight: 500; margin-bottom: 12px; }
/* Security note */
:is(#app, [data-xftp-app]) .security-note {
margin-top: 20px;
padding: 12px;
background: #f0fdf4;
border-radius: 6px;
font-size: .8rem;
color: #555;
text-align: left;
}
:is(#app, [data-xftp-app]) .security-note p + p { margin-top: 6px; }
:is(#app, [data-xftp-app]) .security-note a { color: #3b82f6; text-decoration: none; }
:is(#app, [data-xftp-app]) .security-note a:hover { text-decoration: underline; }
/* ── Dark mode ─────────────────────────────────── */
.dark :is(#app, [data-xftp-app]) {
color: #e5e7eb;
--xftp-ring-bg: #374151;
--xftp-ring-fg: #60a5fa;
--xftp-ring-text: #e5e7eb;
--xftp-ring-done: #4ade80;
}
.dark :is(#app, [data-xftp-app]) .card {
background: #1f2937;
box-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.dark :is(#app, [data-xftp-app]) .drop-zone { border-color: #4b5563; }
.dark :is(#app, [data-xftp-app]) .drop-zone.drag-over {
border-color: #60a5fa;
background: rgba(59,130,246,.15);
}
.dark :is(#app, [data-xftp-app]) .btn-secondary { background: #4b5563; }
.dark :is(#app, [data-xftp-app]) .btn-secondary:hover { background: #374151; }
.dark :is(#app, [data-xftp-app]) .hint { color: #9ca3af; }
.dark :is(#app, [data-xftp-app]) #upload-status,
.dark :is(#app, [data-xftp-app]) #dl-status { color: #9ca3af; }
.dark :is(#app, [data-xftp-app]) .link-row input {
background: #374151;
border-color: #4b5563;
color: #e5e7eb;
}
.dark :is(#app, [data-xftp-app]) .success { color: #4ade80; }
.dark :is(#app, [data-xftp-app]) .error { color: #f87171; }
.dark :is(#app, [data-xftp-app]) .security-note {
background: rgba(34,197,94,.1);
color: #d1d5db;
}
.dark :is(#app, [data-xftp-app]) .upload-link { color: #60a5fa; }
.dark :is(#app, [data-xftp-app]) .security-note a { color: #60a5fa; }
File diff suppressed because one or more lines are too long
+6
View File
@@ -0,0 +1,6 @@
module Main where
import Simplex.FileTransfer.Client.Main
main :: IO ()
main = xftpClientCLI
+53
View File
@@ -0,0 +1,53 @@
packages: .
-- packages: . ../direct-sqlcipher ../sqlcipher-simple
-- packages: . ../hs-socks
-- packages: . ../http2
-- packages: . ../network-transport
-- uncomment two sections below to run tests with coverage
-- package *
-- coverage: True
-- library-coverage: True
-- package attoparsec
-- coverage: False
-- library-coverage: False
index-state: 2023-12-12T00:00:00Z
package cryptostore
flags: +use_crypton
source-repository-package
type: git
location: https://github.com/simplex-chat/aeson.git
tag: aab7b5a14d6c5ea64c64dcaee418de1bb00dcc2b
source-repository-package
type: git
location: https://github.com/simplex-chat/hs-socks.git
tag: a30cc7a79a08d8108316094f8f2f82a0c5e1ac51
source-repository-package
type: git
location: https://github.com/simplex-chat/direct-sqlcipher.git
tag: f814ee68b16a9447fbb467ccc8f29bdd3546bfd9
source-repository-package
type: git
location: https://github.com/simplex-chat/sqlcipher-simple.git
tag: a46bd361a19376c5211f1058908fc0ae6bf42446
-- waiting for published warp-tls-3.4.7
source-repository-package
type: git
location: https://github.com/yesodweb/wai.git
tag: ec5e017d896a78e787a5acea62b37a4e677dec2e
subdir: warp-tls
-- backported fork due http-5.0
source-repository-package
type: git
location: https://github.com/simplex-chat/wai.git
tag: 2f6e5aa5f05ba9140ac99e195ee647b4f7d926b0
subdir: warp
+3
View File
@@ -0,0 +1,3 @@
# Streamlined NTRU Prime: sntrup761
The implementation of sntrup761 is the _exact_ copy from this [Internet draft](https://www.ietf.org/archive/id/draft-josefsson-ntruprime-streamlined-00.html).
Submodule
+1
Submodule cbits/blst added at db3defd0d5
+24
View File
@@ -0,0 +1,24 @@
// SPDX-License-Identifier: Apache-2.0
// getentropy() shim for Windows, where it is absent from the CRT.
// Follows the POSIX contract: fills `buffer` with `length` random bytes
// (length must not exceed 256), returns 0 on success or -1 with errno set.
#ifdef _WIN32
#include <errno.h>
#include <stddef.h>
#include <windows.h>
#include <bcrypt.h>
int getentropy(void *buffer, size_t length) {
if (length > 256) {
errno = EIO;
return -1;
}
NTSTATUS status = BCryptGenRandom(NULL, (PUCHAR)buffer, (ULONG)length,
BCRYPT_USE_SYSTEM_PREFERRED_RNG);
if (!BCRYPT_SUCCESS(status)) {
errno = EIO;
return -1;
}
return 0;
}
#endif
Submodule
+1
Submodule cbits/libbbs added at 59a0f4bf32
+9
View File
@@ -0,0 +1,9 @@
#include <openssl/sha.h>
#include "sha512.h"
void crypto_hash_sha512 (unsigned char *out,
const unsigned char *in,
unsigned long long inlen)
{
SHA512(in, inlen, out);
}
+3
View File
@@ -0,0 +1,3 @@
void crypto_hash_sha512 (unsigned char *out,
const unsigned char *in,
unsigned long long inlen);
+1035
View File
File diff suppressed because it is too large Load Diff
+33
View File
@@ -0,0 +1,33 @@
/*
* Derived from public domain source, written by (in alphabetical order):
* - Daniel J. Bernstein
* - Chitchanok Chuengsatiansup
* - Tanja Lange
* - Christine van Vredendaal
*/
#ifndef SNTRUP761_H
#define SNTRUP761_H
#include <string.h>
#include <stdint.h>
#define SNTRUP761_SECRETKEY_SIZE 1763
#define SNTRUP761_PUBLICKEY_SIZE 1158
#define SNTRUP761_CIPHERTEXT_SIZE 1039
#define SNTRUP761_SIZE 32
typedef void sntrup761_random_func (void *ctx, size_t length, uint8_t *dst);
void
sntrup761_keypair (uint8_t *pk, uint8_t *sk,
void *random_ctx, sntrup761_random_func *random);
void
sntrup761_enc (uint8_t *c, uint8_t *k, const uint8_t *pk,
void *random_ctx, sntrup761_random_func *random);
void
sntrup761_dec (uint8_t *k, const uint8_t *c, const uint8_t *sk);
#endif /* SNTRUP761_H */
+104
View File
@@ -0,0 +1,104 @@
# Coding and building
This file provides guidance on coding style and approaches and on building the code.
## Code Security
When designing code and planning implementations:
- Apply adversarial thinking, and consider what may happen if one of the communicating parties is malicious.
- Formulate an explicit threat model for each change - who can do which undesirable things and under which circumstances.
## Code Quality Standards
Haskell client and server code serves as system specification, not just implementation — we use type-driven design to reflect the business domain in types. Quality, conciseness, and clarity of Haskell code are critical.
## Code Style, Formatting and Approaches
The project uses **fourmolu** for Haskell code formatting. Configuration is in `fourmolu.yaml`.
**Key formatting rules:**
- 2-space indentation
- Trailing function arrows, commas, and import/export style
- Record brace without space: `{field = value}`
- Single newline between declarations
- Never use unicode symbols
- Inline `let` style with right-aligned `in`
**Format code before committing:**
```bash
# Format a single file
fourmolu -i src/Simplex/Messaging/Protocol.hs
```
Some files that use CPP language extension cannot be formatted as a whole, so individual code fragments need to be formatted.
**Follow existing code patterns:**
- Match the style of surrounding code
- Use qualified imports with short aliases (e.g., `import qualified Data.ByteString.Char8 as B`)
- Use record syntax for types with multiple fields
- Prefer explicit pattern matching over partial functions
**Comments policy:**
- Avoid redundant comments that restate what the code already says
- Only comment on non-obvious design decisions or tricky implementation details
- Function names and type signatures should be self-documenting
- Do not add comments like "wire format encoding" (Encoding class is always wire format) or "check if X" when the function name already says that
- Assume a competent Haskell reader
**Diff and refactoring:**
- Avoid unnecessary changes and code movements
- Never do refactoring unless it substantially reduces cost of solving the current problem, including the cost of refactoring
- Aim to minimize the code changes - do what is minimally required to solve users' problems
**Document and code structure:**
- **Never move existing code or sections around** - add new content at appropriate locations without reorganizing existing structure.
- When adding new sections to documents, continue the existing numbering scheme.
- Minimize diff size - prefer small, targeted changes over reorganization.
**Code analysis and review:**
- Trace data flows end-to-end: from origin, through storage/parameters, to consumption. Flag values that are discarded and reconstructed from partial data (e.g. extracted from a URI missing original fields) — this is usually a bug.
- Read implementations of called functions, not just signatures — if duplication involves a called function, check whether decomposing it resolves the duplication.
- Do not save time on analysis. Read every function in the data flow even when the interface seems clear — wrong assumptions about internals are the main source of missed bugs.
### Haskell Extensions
- `StrictData` enabled by default
- Use STM for safe concurrency
- Assume concurrency in PostgreSQL queries
- Comprehensive warning flags with strict pattern matching
## Build Commands
```bash
# Standard build
cabal build
# Fast build
cabal build --ghc-options -O0
# Build specific executables
cabal build exe:smp-server exe:xftp-server exe:ntf-server exe:xftp
# Build with PostgreSQL server support
cabal build -fserver_postgres
# Client-only library build (no server code)
cabal build -fclient_library
# Find binary location
cabal list-bin exe:smp-server
```
### Cabal Flags
- `swift`: Enable Swift JSON format
- `client_library`: Build without server code
- `client_postgres`: Use PostgreSQL instead of SQLite for agent persistence
- `server_postgres`: PostgreSQL support for server queue/notification store
## External Dependencies
Custom forks specified in `cabal.project`:
- `aeson`, `hs-socks` (SimpleX forks)
- `direct-sqlcipher`, `sqlcipher-simple` (encrypted SQLite)
- `warp`, `warp-tls` (HTTP server)
+105
View File
@@ -0,0 +1,105 @@
# SimpleXMQ repository
This file provides guidance on the project structure to help working with code in this repository.
## Project Overview
SimpleXMQ is a Haskell message broker implementing unidirectional (simplex) queues for privacy-preserving messaging.
Key components:
- **SimpleX Messaging Protocol**: SMP protocol definition and encodings ([code](../src/Simplex/Messaging/Protocol.hs), [transport code](../src/Simplex/Messaging/Transport.hs), [spec](../protocol/simplex-messaging.md)).
- **SMP Server**: Message broker with TLS, in-memory queues, optional persistence ([main code](../src/Simplex/Messaging/Server.hs), [all code files](../src/Simplex/Messaging/Server/), [executable](../apps/smp-server/)). For proxying SMP commands the server uses [lightweight SMP client](../src/Simplex/Messaging/Client/Agent.hs).
- **SMP Client**: Functional API with STM-based message delivery ([code](../src/Simplex/Messaging/Client.hs)).
- **SMP Agent**: High-level duplex connections via multiple simplex queues with E2E encryption ([code](../src/Simplex/Messaging/Agent.hs)). Implements Agent-to-agent protocol ([code](../src/Simplex/Messaging/Agent/Protocol.hs), [spec](../protocol/agent-protocol.md)) via intermediary agent client ([code](../src/Simplex/Messaging/Agent/Client.hs)).
- **XFTP**: SimpleX File Transfer Protocol, server and CLI client ([code](../src/Simplex/FileTransfer/), [spec](../protocol/xftp.md)).
- **XRCP**: SimpleX Remote Control Protocol ([code](`../src/Simplex/RemoteControl/`), [spec](../protocol/xrcp.md)).
- **Notifications**: Push notifications server requires PostgreSQL ([code](../src/Simplex/Messaging/Notifications), [executable](../apps/ntf-server/)). Client protocol is used for clients to communicate with the server ([code](../src/Simplex/Messaging/Notifications/Protocol.hs), [spec](../protocol/push-notifications.md)). For subscribing to SMP notifications the server uses [lightweight SMP client](../src/Simplex/Messaging/Client/Agent.hs).
## Architecture
For general overview see `../protocol/overview-tjr.md`.
SMP Protocol Layers:
```
TLS Transport → SMP Protocol → Agent Protocol → Application protocol
```
XFTP Protocol Layers:
```
TLS Transport (HTTP2 encoding) → XFTP Protocol → Out-of-band file descriptions
```
## Key Patterns
1. **Persistence**: All queue state managed via Software Transactional Memory or via PostgreSQL
- `Simplex.Messaging.Server.MsgStore.STM` - in-memory messages
- `Simplex.Messaging.Server.QueueStore.STM` - in-memory queue state
- `Simplex.Messaging.Server.MsgStore.Postgres` - message storage
- `Simplex.Messaging.Server.QueueStore.Postgres` - queue storage
2. **Append-Only Store Log**: Optional persistence via journal for in-memory storage
- `Simplex.Messaging.Server.StoreLog` - queue creation log
- Compacted on restart
3. **Agent Storage**:
- SQLite (default) or PostgreSQL
- Migrations in `src/Simplex/Messaging/Agent/Store/{SQLite,Postgres}/Migrations/`
4. **Protocol Versioning**: All layers support version negotiation
- `Simplex.Messaging.Version` - version range utilities
5. **Double Ratchet E2E**: Per-connection encryption
- `Simplex.Messaging.Crypto.Ratchet`
- SNTRUP761 post-quantum KEM (`src/Simplex/Messaging/Crypto/SNTRUP761/`)
## Source Layout
```
src/Simplex/
├── Messaging/
│ ├── Agent.hs # Main agent (~210KB)
│ ├── Server.hs # SMP server (~130KB)
│ ├── Client.hs # Client API (~65KB)
│ ├── Protocol.hs # Protocol types (~77KB)
│ ├── Crypto.hs # E2E encryption (~52KB)
│ ├── Transport.hs # Transport encoding over TLS
│ ├── Agent/Store/ # SQLite/Postgres persistence
│ ├── Server/ # Server internals (QueueStore, MsgStore, Control)
│ └── Notifications/ # Push notification system
├── FileTransfer/ # XFTP implementation for file transfers
└── RemoteControl/ # XRCP implementation for device discovery & control
```
## Protocol Documentation
- `protocol/overview-tjr.md`: SMP protocols stack overview
- `protocol/simplex-messaging.md`: SMP protocol spec (v19)
- `protocol/agent-protocol.md`: Agent protocol spec (v7)
- `protocol/xftp.md`: File transfer protocol
- `protocol/xrcp.md`: Remote control protocol
- `rfcs/`: Design RFCs for features
## Testing
```bash
# Run all tests
cabal test --test-show-details=streaming
# Run specific test group (uses HSpec)
cabal test --test-option=--match="/Core tests/Encryption tests/"
# Run single test
cabal test --test-option=--match="/SMP client agent/functional API/"
```
Tests require PostgreSQL running on `localhost:5432` when using `-fserver_postgres` or `-fclient_postgres`.
Test files are in `tests/` with structure:
- `Test.hs`: Main runner
- `AgentTests/`: Agent protocol and connection tests
- `CoreTests/`: Crypto, encoding, storage tests
- `ServerTests.hs`: SMP server tests
- `XFTPServerTests.hs`: File transfer tests
+23
View File
@@ -0,0 +1,23 @@
# Contributing to SimpleX repositories
## Focus on user problems
We do not make code changes to improve code - any change must address a specific user problem or request.
## Discuss the plans as early as possible
Please discuss the problem you want to solve and your detailed implementation plan with the project team prior to contributing, to avoid wasted time and additional changes. Acceptance of your contribution depends on your willingness and ability to iterate the proposed contribution to achieve the required quality level, coding style, test coverage, and alignment with user requirements as they are understood by the project team.
## Follow project structure, coding style and approaches
./PROJECT.md has information about the structure of this `simplexmq` repository.
./CODE.md has details about general requirements common for `simplexmq` and `simplex-chat` repositories.
This files can be used with LLM prompts, e.g. if you use Claude Code you can create CLAUDE.md file in project root importing content from these files:
```markdown
@README.md
@contributing/PROJECT.md
@contributing/CODE.md
```
+185
View File
@@ -0,0 +1,185 @@
# SMP agent commands (Duplex protocol commands)
This document describes Duplex commands flow through SMP agent components (see *design/agent.gv*), as well as some interactions between [recipient and sender](#Recipient-and-sender-terminology) clients, clients' SMP agents and SMP servers, and sequencing of Duplex and SMP commands.
Legend:
- > Command flow through SMP agent in blockquotes
- **Q - Design and other questions in bold**
## Table of contents
- [Recipient and sender terminology](#Recipient-and-sender-terminology)
- [Agent design comparison](#agent-design-comparison)
- [Communication between user client and client-side SMP agent](#Communication-between-user-client-and-client-side-SMP-agent)
- [User client commands](#User-client-commands)
- [`create`](#create)
- [`join`](#join)
- [`accept`](#accept)
- [`subscribe`](#subscribe)
- [`getStatus`](#getStatus)
- [`send`](#send)
- [`acknowledge`](#acknowledge)
- [`suspend`](#suspend)
- [`delete`](#delete)
- [Agent messages to user client](#Agent-messages-to-user-client)
- [`connection`](#connection)
- [`connectionInvitation`](#connectionInvitation)
- [`confirmation`](#confirmation)
- [`message`](#message)
- [`unsubscribed`](#unsubscribed)
- [`queueStatus`](#queueStatus)
- [`ok`](#ok)
- [`error`](#error)
- [Communication between SMP agents](#Communication-between-SMP-agents)
- [`helloMsg`](#helloMsg)
- [`replyQueueMsg`](#replyQueueMsg)
- [`deleteQueueMsg`](#deleteQueueMsg)
- [`clientMsg`](#clientMsg)
- [`acknowledgeMsg`](#acknowledgeMsg)
- [General questions](#General-questions)
## Recipient and sender terminology
Here and further recipient is referred to the side that initiates the connection, i.e. creates the initial SMP queue and sends an out-of-band invitation to the sender. Sender is referred to the side that joins this SMP queue through the out-of-band invitation and then creates another SMP queue in the opposite direction for the recipient. This is to distinguish the sides for commands that imply specific direction, conceptually in Duplex connection once connection is established both sides act both as a recipient and a sender through different SMP queues.
## Agent design comparison
| Option | Pros | Cons |
| ------ | ---- | ---- |
| agent.gv | one TCP connection to a specific SMP server host is re-used by multiple users. | responses from the same server to multiple users are mixed and have to be correlated not only with commands but with the users as well, this correlation is not present in the design yet |
| agent2.gv | easier correlation of commands with responses, clear separation of internal user agent API (TBQueues) from TCP api (although this can be done with the first design as well) | if there are multiple users, there may be multiple TCP connections to the same SMP host |
## Communication between user client and client-side SMP agent
### User client commands
#### `create`
🚧 **To be fixed according to agent2.gv**
Requests recipient SMP server to create a new SMP queue.
Is made by the [recipient](#Recipient-and-sender-terminology).
> recipient user client -> user agent TCP socket (main socket) -> runUser thread -> create user connection group (socket and threads) -> user connection group "user" thread -> agent server commands queue -> runServer thread -> SMP `create` command sent to server connection group "send" queue -> server connection group "send" thread -> SMP server connection TCP socket -> recipient's SMP server
- SMP Server then responds with SMP `queueIds` command to SMP server connection TCP socket
- Recipient agent gets automatically subscribed to receive the messages from the queue
- Agent replies to user client with Duplex [`connectionInvitation`](#connectionInvitation) command
- User sends connection invitation out-of-band to sender
- **Q - Shouldn't user connection group be created after server responds, or even after sender joins and invites back?**
A - Agent has to correlate commands with responses anyway (replying on the fact that responses arrive in the same order that the commands were sent, occasionally interleaved with messages), so all the information needed to create the connection will be available in the command buffer anyway. It is probably better to create connection in the agent after the queue was successfully created in the server, and send an error to the user if SMP server responded with error.
- **Q - How does flow go from runUser thread to user connection group "user" thread? Does runUser pass the the initial `create` command further to "user" thread through socket?**
A - runUser does not receive any TCP traffic other than initial TCP handshake, when it does it forks all user group threads. It does not participate in commands/messages flow. Commands go from "user" to "runServer" (via TBQueue), responses,messages and acknowledgements (see protocol changes) from "message subscriber" to "send" (via TBQueue)
- **Q - Does sender agent persist partial info about connection record at this point, or only when receives an opposing queue from the sender?**
A - Yes, it should create connection with one queue (status "PENDING"), the status of the second queue will be "NONE" - see sequence diagram. In types it is `Maybe ConnState` with "NONE" corresponding to `Nothing`, as we don't need to have the second queue record. Also, protocol allows not to have reply queue, so the connection can remain unidirectional.
- **Q - Shouldn't agent at this point answer with Duplex [`connection`](#connection) command? SMP agent commands have conflicting comments in the Duplex protocol. What is that command used for at all at this point?**
A - There is no need to split creating connection and creating the invitation - it was initially two commands, and now it's merged into one - it creates the queue on SMP server, connection record in the agent and the invitation string to be sent out-of-band to the other party - need to fix any contradictions.
#### `join`
🚧 **To be fixed according to agent2.gv**
Replies via out-of-band invitation with sender's key and profile.
Is made by the [sender](#Recipient-and-sender-terminology).
> sender user client -> user agent TCP socket (main socket) -> runUser thread -> create user connection group (socket and threads) -> user connection group "user" thread -> agent server commands queue -> runServer thread -> SMP `send` command sent to server connection group "send" queue, it contains sender's key and profile wrapped in Duplex [`clientMsg`](#clientMsg) command -> server connection group "send" thread -> SMP server connection TCP socket -> recipient's SMP server
- SMP server then delivers sender's message containing key and profile to recipient SMP agent with SMP `message` command
- Recipient agent secures previously created queue with Duplex [`accept`](#accept) command
#### `accept`
🚧 **To be fixed according to agent2.gv**
Secures SMP queue with sender's key.
Is made by the [recipient](#Recipient-and-sender-terminology).
> recipient user client -> user connection TCP socket -> user connection group "user" thread -> agent server commands queue -> runServer thread -> SMP `secure` command sent to server connection group "send" queue -> server connection group "send" thread -> SMP server connection TCP socket -> recipient's SMP server
- Sender's agent at this point repeatedly sends Duplex [`helloMsg`](#helloMsg) command to the recipient SMP server, which should be successful once sender queue is secured by the recipient agent with key provided by the sender in the Duplex [`join`](#join) command
- **Q - Does this command go through user agent TCP socket (main socket)? This relates to the question whether user connection groups for the recipient agent should be created during Duplex [`create`](#create) command**
A - User connection group is created during TCP handshake.
#### `subscribe`
Subscribes/unsubscribes user to/from SMP queue.
> agent client -> user connection TCP socket -> "user receive" thread -> user receive TBQueue -> "process commands" thread -> \*
>
> \* -> commands TBQueue -> client thread; client thread is blocked until receives answer from SMP server -> \#
>
> \* -> srv snd TBQueue -> "server send" thread -> `subscribe` SMP command is sent through SMP client connection TCP socket -> SMP server responds with first available message or if there is none with SMP `ok` command back to SMP client connection TCP socket -> "server receive" thread -> srv rcv TBQueue -> client thread; client thread becomes unblocked -> \#
>
> \# -> user SMP TBQueue -> "process responses" thread -> queue is persisted as Disabled -> Duplex [`ok`](#ok) command sent to user send TBQueue -> "user send" thread -> user connection TCP socket -> agent client
- **Q - Which SMP command is sent to the SMP server on Duplex `subscribe Off`?**
A -
#### `getStatus`
#### `send`
#### `acknowledge`
#### `suspend`
#### `delete`
### Agent messages to user client
TODO
#### `connection`
#### `connectionInvitation`
#### `confirmation`
#### `message`
#### `unsubscribed`
#### `queueStatus`
#### `ok`
#### `error`
## Communication between SMP agents
TODO
#### `helloMsg`
#### `replyQueueMsg`
#### `deleteQueueMsg`
#### `clientMsg`
#### `acknowledgeMsg`
## General questions
- **Q - When does agent set up server connection groups?**
A - When it sees the new hostname:port combination - see the map of servers inside [AgentClient](https://github.com/simplex-chat/simplex-messaging/blob/client/src/Simplex/Messaging/Agent/Env.hs#L33)
- **Q - Replace command keywords with command names in the Duplex protocol sequence diagram?**
A - Let's keep keywords, I also use them as constructors in code, they are part of the actual command syntax.
+79
View File
@@ -0,0 +1,79 @@
digraph SMPAgent {
graph [fontname=arial]
node [fontname=arial fontsize=11 shape=box]
edge [fontname=arial fontsize=10 arrowhead=open arrowtail=open]
subgraph clusterAgent {
label="agent threads"
main [shape=hexagon color=orange label="main\nthread"]
aSock [label="user agent TCP socket" color=darkgreen]
subgraph clusterThreads {
label=""
node [shape=hexagon color=orange]
runUser [label="runUser\nthread"]
runServer [label="runServer\nthread"]
msgSubscriber [label="message\nsubscriber\nthread"]
}
main -> {runUser runServer} [style=dashed label=race color=orange fontcolor=orange]
aSock -> runUser [color=darkgreen]
acInq [shape=larrow label="agent\nserver\ncommands\nTBQueue"]
acInq -> runServer [color=darkgreen]
runServer -> amInq [color=darkgreen label="unknown\nconnection\nor\nwrong\nconnection\nstate"]
amInq [shape=rarrow label="agent\nserver\nmessages\nTBQueue"]
amInq -> msgSubscriber [color=blue]
msgSubscriber -> connectionsStore [dir=both]
runUser -> connectionsStore [dir=both]
runServer -> connectionsStore [dir=both]
connectionsStore [shape=cylinder label="duplex\nconnection\nrecords"]
}
subgraph clusterUser {
label="1 group per user connection"
uSock [label="user connection TCP socket" color=darkgreen]
subgraph clusterUserThreads {
graph [fontsize=11]
label="user threads"
node [shape=hexagon, color=orange]
uAgent [label="user"]
uSnd [label="send"]
}
runUser -> uSock [style=dashed label="connect" color=darkgreen fontcolor=darkgreen]
runUser -> {uAgent uSnd} [style=dashed label=race color=orange fontcolor=orange]
uOutq [shape=larrow label="send\nTBQueue"]
uSock -> uAgent [color=darkgreen]
uAgent -> uOutq [color=darkgreen label="SMAP\ncommand\nsyntax\nerror"]
uOutq -> uSnd -> uSock [color=darkgreen]
msgSubscriber -> uOutq [color=blue label="SMAP\nmessages\nand\ncommand\nresponses"]
uAgent -> acInq [color=darkgreen label="SMAP\nprotocol\ncommand"]
}
subgraph clusterServer {
label="1 group per SMP server connection"
sSock [label="SMP server connection TCP socket" color=blue]
subgraph clusterServerThreads {
graph [fontsize=11]
label="server threads"
node [shape=hexagon, color=orange]
sAgent [label="server"]
sSnd [label="send"]
}
runServer -> {sAgent sSnd} [style=dashed label=race color=orange fontcolor=orange]
sOutq [shape=larrow label="send\nTBQueue"]
sSock -> sAgent [color=blue]
runServer -> sOutq [color=darkgreen label="SMP\nprotocol\ncommand"]
sOutq -> sSnd -> sSock [color=blue]
sAgent -> amInq [color=blue label="wrapped\nSMP\nmessages\nand\ncommand\nresponses"]
}
}
+99
View File
@@ -0,0 +1,99 @@
digraph SMPAgent {
graph [fontname=arial]
node [fontname=arial fontsize=11 shape=box]
edge [fontname=arial fontsize=10 arrowhead=open arrowtail=open]
main [shape=hexagon color=orange label="main\nthread"]
aSock [label="user agent TCP socket" color=darkgreen]
subgraph clusterPersistence {
graph [fontsize=11 color=gray]
label="persistence (sqlite)"
connectionsStore [shape=cylinder label="duplex connections,\nSMP queues,\nrecent messages"]
}
subgraph clusterAgent {
graph [color=gray]
node [shape=hexagon color=orange]
label="agent threads"
connectClnt
runClnt
}
main -> {runClnt connectClnt} [style=dashed label="race" color=orange fontcolor=orange]
aSock -> connectClnt [color=darkgreen]
subgraph clusterUserTCP {
graph [color=gray]
label="1 group per user TCP connection"
uSock [label="user connection TCP socket" color=darkgreen]
subgraph clusterUserTCPThreads {
graph [fontsize=11 color=gray]
label="user TCP threads"
node [shape=hexagon, color=orange]
uRcv [label="user\nreceive"]
uSnd [label="user\nsend"]
}
uSock -> uRcv [color=darkgreen]
uSnd -> uSock [color=darkgreen]
}
subgraph clusterUser {
graph [color=gray]
label="1 group per user TCP connection"
subgraph clusterUserInterface {
graph [fontsize=11]
label="user queues"
uInq [shape=rarrow label="user\nreceive\nTBQueue"]
uOutq [shape=larrow label="user\nsend\nTBQueue"]
}
subgraph clusterUserThreads {
graph [fontsize=11]
label="user threads\nNote: `user agent` sends\nall commands to `commands TBQueue`s\n(invalid commands with attached responses),\nand only valid commands to `server TBQueue`.\nIt is used to respond in correct order."
node [shape=hexagon, color=orange]
uAgent [label="user\nagent"]
uProcess [label="process\nresponses"]
}
uRcv -> uInq -> uAgent [color=darkgreen]
uProcess -> uOutq -> uSnd [color=darkgreen]
{uRcv uAgent} -> uOutq [color=green]
uRespq [shape=rarrow label="user\nSMP\nTBQueue"]
subgraph clusterClient {
label="1 group per SMP client/server connection"
runClient [shape=hexagon, color=orange]
sOutq [shape=rarrow label="srv send\nTBQueue"]
sSock [label="SMP client connection TCP socket" color=blue]
subgraph clusterServerThreads {
graph [fontsize=11]
label="SMP client threads"
node [shape=hexagon, color=orange]
sAgent [label="server\nreceive"]
sSnd [label="server\nsend"]
}
runClient -> {sAgent sSnd} [style=dashed label=race color=orange fontcolor=orange]
{uAgent uProcess} -> sOutq -> sSnd -> sSock -> sAgent [color=blue]
}
userState [label="connected\nservers,\nsubscribed\nqueues,\nsent\ncommands\n(STM)" shape="folder"]
{uAgent uProcess} -> userState [dir=both color="#FF8888"]
{uAgent uProcess} -> connectionsStore [dir=both color="#880000"]
connectClnt -> uSock [style=dashed label="connect" color=darkgreen fontcolor=darkgreen]
connectClnt -> {uRcv uSnd} [style=dashed label="race" color=orange fontcolor=orange]
runClnt -> {uAgent uProcess} [style=dashed label="race" color=orange fontcolor=orange]
sAgent -> uRespq -> uProcess
uAgent -> runClient [style=dashed label="fork" color=orange fontcolor=orange]
}
}
+394
View File
@@ -0,0 +1,394 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: SMPAgent Pages: 1 -->
<svg width="1073pt" height="1142pt"
viewBox="0.00 0.00 1073.00 1142.34" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 1138.3374)">
<title>SMPAgent</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-1138.3374 1069,-1138.3374 1069,4 -4,4"/>
<g id="clust1" class="cluster">
<title>clusterPersistence</title>
<polygon fill="none" stroke="#c0c0c0" points="927,-512.7374 927,-615.3874 1057,-615.3874 1057,-512.7374 927,-512.7374"/>
<text text-anchor="middle" x="992" y="-601.4874" font-family="arial" font-size="11.00" fill="#000000">persistence (sqlite)</text>
</g>
<g id="clust2" class="cluster">
<title>clusterAgent</title>
<polygon fill="none" stroke="#c0c0c0" points="175,-963.3624 175,-1040.1624 389,-1040.1624 389,-963.3624 175,-963.3624"/>
<text text-anchor="middle" x="282" y="-1023.5624" font-family="arial" font-size="14.00" fill="#000000">agent threads</text>
</g>
<g id="clust4" class="cluster">
<title>clusterUserTCP</title>
<polygon fill="none" stroke="#c0c0c0" points="8,-509.375 8,-933.3624 230,-933.3624 230,-509.375 8,-509.375"/>
<text text-anchor="middle" x="119" y="-916.7624" font-family="arial" font-size="14.00" fill="#000000">1 group per user TCP connection</text>
</g>
<g id="clust5" class="cluster">
<title>clusterUserTCPThreads</title>
<polygon fill="none" stroke="#c0c0c0" points="38,-517.375 38,-610.7499 222,-610.7499 222,-517.375 38,-517.375"/>
<text text-anchor="middle" x="130" y="-596.8499" font-family="arial" font-size="11.00" fill="#000000">user TCP threads</text>
</g>
<g id="clust6" class="cluster">
<title>clusterUser</title>
<polygon fill="none" stroke="#c0c0c0" points="250,-8 250,-826.5624 919,-826.5624 919,-8 250,-8"/>
<text text-anchor="middle" x="584.5" y="-809.9624" font-family="arial" font-size="14.00" fill="#000000">1 group per user TCP connection</text>
</g>
<g id="clust7" class="cluster">
<title>clusterUserInterface</title>
<polygon fill="none" stroke="#c0c0c0" points="258,-400.375 258,-485.175 419,-485.175 419,-400.375 258,-400.375"/>
<text text-anchor="middle" x="338.5" y="-471.275" font-family="arial" font-size="11.00" fill="#000000">user queues</text>
</g>
<g id="clust8" class="cluster">
<title>clusterUserThreads</title>
<polygon fill="none" stroke="#c0c0c0" points="622,-634.3874 622,-793.7624 865,-793.7624 865,-634.3874 622,-634.3874"/>
<text text-anchor="middle" x="743.5" y="-779.8624" font-family="arial" font-size="11.00" fill="#000000">user threads</text>
<text text-anchor="middle" x="743.5" y="-766.6624" font-family="arial" font-size="11.00" fill="#000000">Note: `user agent` sends</text>
<text text-anchor="middle" x="743.5" y="-753.4624" font-family="arial" font-size="11.00" fill="#000000">all commands to `commands TBQueue`s</text>
<text text-anchor="middle" x="743.5" y="-740.2624" font-family="arial" font-size="11.00" fill="#000000">(invalid commands with attached responses),</text>
<text text-anchor="middle" x="743.5" y="-727.0624" font-family="arial" font-size="11.00" fill="#000000">and only valid commands to `server TBQueue`.</text>
<text text-anchor="middle" x="743.5" y="-713.8624" font-family="arial" font-size="11.00" fill="#000000">It is used to respond in correct order.</text>
</g>
<g id="clust10" class="cluster">
<title>clusterClient</title>
<polygon fill="none" stroke="#c0c0c0" points="427,-128.6 427,-482.975 812,-482.975 812,-128.6 427,-128.6"/>
<text text-anchor="middle" x="619.5" y="-466.375" font-family="arial" font-size="14.00" fill="#000000">1 group per SMP client/server connection</text>
</g>
<g id="clust11" class="cluster">
<title>clusterServerThreads</title>
<polygon fill="none" stroke="#c0c0c0" points="564,-136.6 564,-229.975 758,-229.975 758,-136.6 564,-136.6"/>
<text text-anchor="middle" x="661" y="-216.075" font-family="arial" font-size="11.00" fill="#000000">SMP client threads</text>
</g>
<!-- main -->
<g id="node1" class="node">
<title>main</title>
<polygon fill="none" stroke="#ffa500" points="382.542,-1106.2499 363.271,-1134.4251 324.729,-1134.4251 305.458,-1106.2499 324.729,-1078.0747 363.271,-1078.0747 382.542,-1106.2499"/>
<text text-anchor="middle" x="344" y="-1109.5499" font-family="arial" font-size="11.00" fill="#000000">main</text>
<text text-anchor="middle" x="344" y="-1096.3499" font-family="arial" font-size="11.00" fill="#000000">thread</text>
</g>
<!-- connectClnt -->
<g id="node4" class="node">
<title>connectClnt</title>
<polygon fill="none" stroke="#ffa500" points="288.8246,-989.3624 262.4123,-1007.3624 209.5877,-1007.3624 183.1754,-989.3624 209.5877,-971.3624 262.4123,-971.3624 288.8246,-989.3624"/>
<text text-anchor="middle" x="236" y="-986.0624" font-family="arial" font-size="11.00" fill="#000000">connectClnt</text>
</g>
<!-- main&#45;&gt;connectClnt -->
<g id="edge1" class="edge">
<title>main&#45;&gt;connectClnt</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M321.6223,-1082.0307C303.5136,-1062.4318 278.0793,-1034.9044 259.6406,-1014.9484"/>
<polygon fill="#ffa500" stroke="#ffa500" points="252.7011,-1007.4378 262.7926,-1011.7288 256.0942,-1011.1102 259.4874,-1014.7826 259.4874,-1014.7826 259.4874,-1014.7826 256.0942,-1011.1102 256.1823,-1017.8365 252.7011,-1007.4378 252.7011,-1007.4378"/>
<text text-anchor="middle" x="308.7235" y="-1051.1624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- runClnt -->
<g id="node5" class="node">
<title>runClnt</title>
<polygon fill="none" stroke="#ffa500" points="380.5019,-989.3624 362.251,-1007.3624 325.749,-1007.3624 307.4981,-989.3624 325.749,-971.3624 362.251,-971.3624 380.5019,-989.3624"/>
<text text-anchor="middle" x="344" y="-986.0624" font-family="arial" font-size="11.00" fill="#000000">runClnt</text>
</g>
<!-- main&#45;&gt;runClnt -->
<g id="edge2" class="edge">
<title>main&#45;&gt;runClnt</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M344,-1077.9547C344,-1059.6552 344,-1035.9253 344,-1017.631"/>
<polygon fill="#ffa500" stroke="#ffa500" points="344,-1007.5699 348.5001,-1017.5699 344,-1012.5699 344.0001,-1017.5699 344.0001,-1017.5699 344.0001,-1017.5699 344,-1012.5699 339.5001,-1017.57 344,-1007.5699 344,-1007.5699"/>
<text text-anchor="middle" x="353.7235" y="-1051.1624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- aSock -->
<g id="node2" class="node">
<title>aSock</title>
<polygon fill="none" stroke="#006400" points="287.8624,-1124.2499 160.1376,-1124.2499 160.1376,-1088.2499 287.8624,-1088.2499 287.8624,-1124.2499"/>
<text text-anchor="middle" x="224" y="-1102.9499" font-family="arial" font-size="11.00" fill="#000000">user agent TCP socket</text>
</g>
<!-- aSock&#45;&gt;connectClnt -->
<g id="edge3" class="edge">
<title>aSock&#45;&gt;connectClnt</title>
<path fill="none" stroke="#006400" d="M225.8487,-1088.2424C227.781,-1069.421 230.8399,-1039.6253 233.0952,-1017.6565"/>
<polygon fill="#006400" stroke="#006400" points="234.1294,-1007.583 237.5845,-1017.9903 233.6188,-1012.5568 233.1081,-1017.5307 233.1081,-1017.5307 233.1081,-1017.5307 233.6188,-1012.5568 228.6316,-1017.0711 234.1294,-1007.583 234.1294,-1007.583"/>
</g>
<!-- connectionsStore -->
<g id="node3" class="node">
<title>connectionsStore</title>
<path fill="none" stroke="#000000" d="M1048.7017,-580.4228C1048.7017,-583.7287 1023.2871,-586.414 992,-586.414 960.7129,-586.414 935.2983,-583.7287 935.2983,-580.4228 935.2983,-580.4228 935.2983,-526.5021 935.2983,-526.5021 935.2983,-523.1962 960.7129,-520.5109 992,-520.5109 1023.2871,-520.5109 1048.7017,-523.1962 1048.7017,-526.5021 1048.7017,-526.5021 1048.7017,-580.4228 1048.7017,-580.4228"/>
<path fill="none" stroke="#000000" d="M1048.7017,-580.4228C1048.7017,-577.117 1023.2871,-574.4316 992,-574.4316 960.7129,-574.4316 935.2983,-577.117 935.2983,-580.4228"/>
<text text-anchor="middle" x="992" y="-563.3624" font-family="arial" font-size="11.00" fill="#000000">duplex connections,</text>
<text text-anchor="middle" x="992" y="-550.1624" font-family="arial" font-size="11.00" fill="#000000">SMP queues,</text>
<text text-anchor="middle" x="992" y="-536.9624" font-family="arial" font-size="11.00" fill="#000000">recent messages</text>
</g>
<!-- uSock -->
<g id="node6" class="node">
<title>uSock</title>
<polygon fill="none" stroke="#006400" points="169.5337,-900.5624 16.4663,-900.5624 16.4663,-864.5624 169.5337,-864.5624 169.5337,-900.5624"/>
<text text-anchor="middle" x="93" y="-879.2624" font-family="arial" font-size="11.00" fill="#000000">user connection TCP socket</text>
</g>
<!-- connectClnt&#45;&gt;uSock -->
<g id="edge23" class="edge">
<title>connectClnt&#45;&gt;uSock</title>
<path fill="none" stroke="#006400" stroke-dasharray="5,2" d="M198.6778,-978.637C182.4066,-972.8176 163.7253,-964.4984 148.985,-953.3624 133.1431,-941.3941 119.2685,-923.9297 109.2945,-909.3413"/>
<polygon fill="#006400" stroke="#006400" points="103.6056,-900.7046 112.8644,-906.5804 106.356,-904.8801 109.1064,-909.0557 109.1064,-909.0557 109.1064,-909.0557 106.356,-904.8801 105.3483,-911.5311 103.6056,-900.7046 103.6056,-900.7046"/>
<text text-anchor="middle" x="165.5075" y="-944.3624" font-family="arial" font-size="10.00" fill="#006400">connect</text>
</g>
<!-- uRcv -->
<g id="node7" class="node">
<title>uRcv</title>
<polygon fill="none" stroke="#ffa500" points="214.0134,-553.4624 193.0067,-581.6377 150.9933,-581.6377 129.9866,-553.4624 150.9933,-525.2872 193.0067,-525.2872 214.0134,-553.4624"/>
<text text-anchor="middle" x="172" y="-556.7624" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="172" y="-543.5624" font-family="arial" font-size="11.00" fill="#000000">receive</text>
</g>
<!-- connectClnt&#45;&gt;uRcv -->
<g id="edge24" class="edge">
<title>connectClnt&#45;&gt;uRcv</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M237.1238,-971.0203C239.9206,-916.7089 244.541,-752.4513 208,-623.3874 204.8674,-612.323 199.8997,-600.9707 194.6434,-590.7174"/>
<polygon fill="#ffa500" stroke="#ffa500" points="189.7794,-581.6499 198.472,-588.3349 192.143,-586.056 194.5065,-590.4621 194.5065,-590.4621 194.5065,-590.4621 192.143,-586.056 190.541,-592.5893 189.7794,-581.6499 189.7794,-581.6499"/>
<text text-anchor="middle" x="247.7235" y="-837.5624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- uSnd -->
<g id="node8" class="node">
<title>uSnd</title>
<polygon fill="none" stroke="#ffa500" points="111.5662,-553.4624 95.2831,-581.6377 62.7169,-581.6377 46.4338,-553.4624 62.7169,-525.2872 95.2831,-525.2872 111.5662,-553.4624"/>
<text text-anchor="middle" x="79" y="-556.7624" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="79" y="-543.5624" font-family="arial" font-size="11.00" fill="#000000">send</text>
</g>
<!-- connectClnt&#45;&gt;uSnd -->
<g id="edge25" class="edge">
<title>connectClnt&#45;&gt;uSnd</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M232.6954,-971.3264C227.2453,-941.3316 216.1632,-879.2768 208,-826.5624 201.0253,-781.5225 212.2627,-655.5799 180,-623.3874 161.268,-604.6963 143.493,-629.327 121,-615.3874 111.1934,-609.31 103.1361,-599.9964 96.7947,-590.4234"/>
<polygon fill="#ffa500" stroke="#ffa500" points="91.4524,-581.6443 100.4951,-587.8476 94.0516,-585.9157 96.6509,-590.187 96.6509,-590.187 96.6509,-590.187 94.0516,-585.9157 92.8067,-592.5263 91.4524,-581.6443 91.4524,-581.6443"/>
<text text-anchor="middle" x="219.7235" y="-837.5624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- uAgent -->
<g id="node11" class="node">
<title>uAgent</title>
<polygon fill="none" stroke="#ffa500" points="715.5622,-670.4749 697.7811,-698.6502 662.2189,-698.6502 644.4378,-670.4749 662.2189,-642.2997 697.7811,-642.2997 715.5622,-670.4749"/>
<text text-anchor="middle" x="680" y="-673.7749" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="680" y="-660.5749" font-family="arial" font-size="11.00" fill="#000000">agent</text>
</g>
<!-- runClnt&#45;&gt;uAgent -->
<g id="edge26" class="edge">
<title>runClnt&#45;&gt;uAgent</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M363.0275,-971.304C418.9032,-918.2741 582.8091,-762.7159 650.3686,-698.5972"/>
<polygon fill="#ffa500" stroke="#ffa500" points="657.7387,-691.6025 653.5831,-701.7505 654.112,-695.0444 650.4853,-698.4864 650.4853,-698.4864 650.4853,-698.4864 654.112,-695.0444 647.3875,-695.2224 657.7387,-691.6025 657.7387,-691.6025"/>
<text text-anchor="middle" x="482.7235" y="-879.5624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- uProcess -->
<g id="node12" class="node">
<title>uProcess</title>
<polygon fill="none" stroke="#ffa500" points="849.4801,-670.4749 822.24,-698.6502 767.76,-698.6502 740.5199,-670.4749 767.76,-642.2997 822.24,-642.2997 849.4801,-670.4749"/>
<text text-anchor="middle" x="795" y="-673.7749" font-family="arial" font-size="11.00" fill="#000000">process</text>
<text text-anchor="middle" x="795" y="-660.5749" font-family="arial" font-size="11.00" fill="#000000">responses</text>
</g>
<!-- runClnt&#45;&gt;uProcess -->
<g id="edge27" class="edge">
<title>runClnt&#45;&gt;uProcess</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M375.4171,-984.2599C446.9624,-971.0733 623.6511,-929.4235 725,-826.5624 757.4412,-793.6372 776.3095,-743.0546 786.1177,-708.4539"/>
<polygon fill="#ffa500" stroke="#ffa500" points="788.7618,-698.7069 790.4867,-709.5363 787.4527,-703.5325 786.1436,-708.3581 786.1436,-708.3581 786.1436,-708.3581 787.4527,-703.5325 781.8006,-707.1799 788.7618,-698.7069 788.7618,-698.7069"/>
<text text-anchor="middle" x="686.7235" y="-879.5624" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- uSock&#45;&gt;uRcv -->
<g id="edge4" class="edge">
<title>uSock&#45;&gt;uRcv</title>
<path fill="none" stroke="#006400" d="M97.3599,-864.3998C109.8154,-812.5125 145.7537,-662.8 162.808,-591.7545"/>
<polygon fill="#006400" stroke="#006400" points="165.1822,-581.8642 167.2236,-592.6383 164.0151,-586.7261 162.848,-591.5879 162.848,-591.5879 162.848,-591.5879 164.0151,-586.7261 158.4723,-590.5375 165.1822,-581.8642 165.1822,-581.8642"/>
</g>
<!-- uInq -->
<g id="node9" class="node">
<title>uInq</title>
<polygon fill="none" stroke="#000000" points="392.5723,-449.7766 347.4277,-449.7766 347.4277,-414.5733 392.5723,-414.5733 392.5723,-408.5733 410.5723,-432.175 392.5723,-455.7766 392.5723,-449.7766"/>
<text text-anchor="middle" x="379" y="-442.075" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="379" y="-428.875" font-family="arial" font-size="11.00" fill="#000000">receive</text>
<text text-anchor="middle" x="379" y="-415.675" font-family="arial" font-size="11.00" fill="#000000">TBQueue</text>
</g>
<!-- uRcv&#45;&gt;uInq -->
<g id="edge6" class="edge">
<title>uRcv&#45;&gt;uInq</title>
<path fill="none" stroke="#006400" d="M207.752,-544.8492C243.6388,-534.9021 299.1678,-515.8485 339,-485.175 346.5794,-479.3383 353.4481,-471.7165 359.2704,-464.0802"/>
<polygon fill="#006400" stroke="#006400" points="365.1617,-455.8721 362.9865,-466.6201 362.2462,-459.9341 359.3307,-463.9961 359.3307,-463.9961 359.3307,-463.9961 362.2462,-459.9341 355.6749,-461.3722 365.1617,-455.8721 365.1617,-455.8721"/>
</g>
<!-- uOutq -->
<g id="node10" class="node">
<title>uOutq</title>
<polygon fill="none" stroke="#000000" points="329.5723,-449.7766 284.4277,-449.7766 284.4277,-455.7766 266.4277,-432.175 284.4277,-408.5733 284.4277,-414.5733 329.5723,-414.5733 329.5723,-449.7766"/>
<text text-anchor="middle" x="298" y="-442.075" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="298" y="-428.875" font-family="arial" font-size="11.00" fill="#000000">send</text>
<text text-anchor="middle" x="298" y="-415.675" font-family="arial" font-size="11.00" fill="#000000">TBQueue</text>
</g>
<!-- uRcv&#45;&gt;uOutq -->
<g id="edge10" class="edge">
<title>uRcv&#45;&gt;uOutq</title>
<path fill="none" stroke="#00ff00" d="M196.5932,-529.7891C216.4531,-510.6719 244.5472,-483.6285 266.1304,-462.8527"/>
<polygon fill="#00ff00" stroke="#00ff00" points="273.4688,-455.7886 269.3851,-465.9658 269.8666,-459.2562 266.2643,-462.7237 266.2643,-462.7237 266.2643,-462.7237 269.8666,-459.2562 263.1435,-459.4817 273.4688,-455.7886 273.4688,-455.7886"/>
</g>
<!-- uSnd&#45;&gt;uSock -->
<g id="edge5" class="edge">
<title>uSnd&#45;&gt;uSock</title>
<path fill="none" stroke="#006400" d="M80.2082,-581.8642C82.8558,-644.1008 89.1434,-791.9036 91.7913,-854.15"/>
<polygon fill="#006400" stroke="#006400" points="92.2274,-864.3998 87.3063,-854.6001 92.0148,-859.4043 91.8022,-854.4088 91.8022,-854.4088 91.8022,-854.4088 92.0148,-859.4043 96.2982,-854.2175 92.2274,-864.3998 92.2274,-864.3998"/>
</g>
<!-- uInq&#45;&gt;uAgent -->
<g id="edge7" class="edge">
<title>uInq&#45;&gt;uAgent</title>
<path fill="none" stroke="#006400" d="M396.6501,-455.9955C404.2553,-465.5484 413.5612,-476.3588 423,-485.175 495.6201,-553.0045 593.7284,-617.3782 645.1887,-649.3958"/>
<polygon fill="#006400" stroke="#006400" points="653.9378,-654.8067 643.0659,-653.374 649.6853,-652.1767 645.4329,-649.5468 645.4329,-649.5468 645.4329,-649.5468 649.6853,-652.1767 647.7999,-645.7196 653.9378,-654.8067 653.9378,-654.8067"/>
</g>
<!-- uOutq&#45;&gt;uSnd -->
<g id="edge9" class="edge">
<title>uOutq&#45;&gt;uSnd</title>
<path fill="none" stroke="#006400" d="M266.3235,-441.7172C229.3231,-453.7931 167.2864,-476.9926 121,-509.375 115.6566,-513.1132 110.4497,-517.6098 105.6237,-522.2822"/>
<polygon fill="#006400" stroke="#006400" points="98.3887,-529.6856 102.1596,-519.3885 101.8833,-526.1096 105.378,-522.5337 105.378,-522.5337 105.378,-522.5337 101.8833,-526.1096 108.5964,-525.6789 98.3887,-529.6856 98.3887,-529.6856"/>
</g>
<!-- uAgent&#45;&gt;connectionsStore -->
<g id="edge21" class="edge">
<title>uAgent&#45;&gt;connectionsStore</title>
<path fill="none" stroke="#880000" d="M711.4852,-644.5965C717.9336,-640.4916 724.9231,-636.8075 732,-634.3874 772.2162,-620.6351 884.2913,-641.9405 923,-624.3874 937.8878,-617.6364 951.2411,-606.1048 962.1439,-594.2794"/>
<polygon fill="#880000" stroke="#880000" points="703.07,-650.3759 708.7656,-641.0051 707.1916,-647.5452 711.3132,-644.7146 711.3132,-644.7146 711.3132,-644.7146 707.1916,-647.5452 713.8608,-648.424 703.07,-650.3759 703.07,-650.3759"/>
<polygon fill="#880000" stroke="#880000" points="968.9461,-586.5133 965.7424,-597.0007 965.6517,-590.2745 962.3573,-594.0358 962.3573,-594.0358 962.3573,-594.0358 965.6517,-590.2745 958.9722,-591.0708 968.9461,-586.5133 968.9461,-586.5133"/>
</g>
<!-- uAgent&#45;&gt;uOutq -->
<g id="edge11" class="edge">
<title>uAgent&#45;&gt;uOutq</title>
<path fill="none" stroke="#00ff00" d="M647.3434,-665.3697C591.4951,-656.2129 480.4257,-636.0193 447,-615.3874 386.1848,-577.8496 339.1057,-506.4848 315.2537,-464.7649"/>
<polygon fill="#00ff00" stroke="#00ff00" points="310.2445,-455.8352 319.0616,-462.3552 312.6907,-460.196 315.1369,-464.5567 315.1369,-464.5567 315.1369,-464.5567 312.6907,-460.196 311.2123,-466.7583 310.2445,-455.8352 310.2445,-455.8352"/>
</g>
<!-- runClient -->
<g id="node14" class="node">
<title>runClient</title>
<polygon fill="none" stroke="#ffa500" points="722.7244,-432.175 701.3622,-450.175 658.6378,-450.175 637.2756,-432.175 658.6378,-414.175 701.3622,-414.175 722.7244,-432.175"/>
<text text-anchor="middle" x="680" y="-428.875" font-family="arial" font-size="11.00" fill="#000000">runClient</text>
</g>
<!-- uAgent&#45;&gt;runClient -->
<g id="edge30" class="edge">
<title>uAgent&#45;&gt;runClient</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M680,-642.203C680,-596.3198 680,-506.1699 680,-460.3495"/>
<polygon fill="#ffa500" stroke="#ffa500" points="680,-450.2297 684.5001,-460.2297 680,-455.2297 680.0001,-460.2297 680.0001,-460.2297 680.0001,-460.2297 680,-455.2297 675.5001,-460.2298 680,-450.2297 680,-450.2297"/>
<text text-anchor="middle" x="688.3335" y="-550.4624" font-family="arial" font-size="10.00" fill="#ffa500">fork</text>
</g>
<!-- sOutq -->
<g id="node15" class="node">
<title>sOutq</title>
<polygon fill="none" stroke="#000000" points="785.5723,-444.175 740.4277,-444.175 740.4277,-420.175 785.5723,-420.175 785.5723,-414.175 803.5723,-432.175 785.5723,-450.175 785.5723,-444.175"/>
<text text-anchor="middle" x="772" y="-435.475" font-family="arial" font-size="11.00" fill="#000000">srv send</text>
<text text-anchor="middle" x="772" y="-422.275" font-family="arial" font-size="11.00" fill="#000000">TBQueue</text>
</g>
<!-- uAgent&#45;&gt;sOutq -->
<g id="edge14" class="edge">
<title>uAgent&#45;&gt;sOutq</title>
<path fill="none" stroke="#0000ff" d="M691.5879,-642.1215C694.8569,-633.6035 698.2645,-624.1836 701,-615.3874 716.6229,-565.151 708.4801,-548.8178 729,-500.375 735.1504,-485.8551 744.2149,-470.9073 752.4376,-458.7199"/>
<polygon fill="#0000ff" stroke="#0000ff" points="758.2226,-450.3792 756.2211,-461.1608 755.373,-454.4877 752.5234,-458.5962 752.5234,-458.5962 752.5234,-458.5962 755.373,-454.4877 748.8257,-456.0316 758.2226,-450.3792 758.2226,-450.3792"/>
</g>
<!-- userState -->
<g id="node19" class="node">
<title>userState</title>
<polygon fill="none" stroke="#000000" points="890.7837,-482.5758 887.7837,-486.5758 866.7837,-486.5758 863.7837,-482.5758 821.2163,-482.5758 821.2163,-381.7742 890.7837,-381.7742 890.7837,-482.5758"/>
<text text-anchor="middle" x="856" y="-468.475" font-family="arial" font-size="11.00" fill="#000000">connected</text>
<text text-anchor="middle" x="856" y="-455.275" font-family="arial" font-size="11.00" fill="#000000">servers,</text>
<text text-anchor="middle" x="856" y="-442.075" font-family="arial" font-size="11.00" fill="#000000">subscribed</text>
<text text-anchor="middle" x="856" y="-428.875" font-family="arial" font-size="11.00" fill="#000000">queues,</text>
<text text-anchor="middle" x="856" y="-415.675" font-family="arial" font-size="11.00" fill="#000000">sent</text>
<text text-anchor="middle" x="856" y="-402.475" font-family="arial" font-size="11.00" fill="#000000">commands</text>
<text text-anchor="middle" x="856" y="-389.275" font-family="arial" font-size="11.00" fill="#000000">(STM)</text>
</g>
<!-- uAgent&#45;&gt;userState -->
<g id="edge19" class="edge">
<title>uAgent&#45;&gt;userState</title>
<path fill="none" stroke="#ff8888" d="M705.4839,-635.9704C734.1546,-597.1508 781.1323,-533.5442 814.9848,-487.7086"/>
<polygon fill="#ff8888" stroke="#ff8888" points="699.5075,-644.0622 701.8288,-633.3448 702.478,-640.0402 705.4485,-636.0182 705.4485,-636.0182 705.4485,-636.0182 702.478,-640.0402 709.0683,-638.6917 699.5075,-644.0622 699.5075,-644.0622"/>
<polygon fill="#ff8888" stroke="#ff8888" points="820.9337,-479.654 818.6124,-490.3714 817.9632,-483.6759 814.9927,-487.6979 814.9927,-487.6979 814.9927,-487.6979 817.9632,-483.6759 811.3729,-485.0245 820.9337,-479.654 820.9337,-479.654"/>
</g>
<!-- uProcess&#45;&gt;connectionsStore -->
<g id="edge22" class="edge">
<title>uProcess&#45;&gt;connectionsStore</title>
<path fill="none" stroke="#880000" d="M851.5097,-660.0083C892.5279,-651.4468 943.5859,-638.4864 960,-624.3874 968.7086,-616.9071 975.1618,-606.5771 979.8973,-596.1137"/>
<polygon fill="#880000" stroke="#880000" points="841.5933,-662.0345 850.49,-655.6236 846.4921,-661.0335 851.3909,-660.0325 851.3909,-660.0325 851.3909,-660.0325 846.4921,-661.0335 852.2918,-664.4414 841.5933,-662.0345 841.5933,-662.0345"/>
<polygon fill="#880000" stroke="#880000" points="983.7787,-586.5449 984.1898,-597.5031 981.8992,-591.1782 980.0198,-595.8116 980.0198,-595.8116 980.0198,-595.8116 981.8992,-591.1782 975.8498,-594.12 983.7787,-586.5449 983.7787,-586.5449"/>
</g>
<!-- uProcess&#45;&gt;uOutq -->
<g id="edge8" class="edge">
<title>uProcess&#45;&gt;uOutq</title>
<path fill="none" stroke="#006400" d="M782.4156,-642.109C763.1961,-600.1829 726.1195,-524.9349 701,-509.375 632.2716,-466.8022 409.1254,-523.6095 338,-485.175 329.0427,-480.3347 321.505,-472.5258 315.4734,-464.3728"/>
<polygon fill="#006400" stroke="#006400" points="309.7367,-455.862 319.0575,-461.639 312.5314,-460.0081 315.326,-464.1542 315.326,-464.1542 315.326,-464.1542 312.5314,-460.0081 311.5946,-466.6694 309.7367,-455.862 309.7367,-455.862"/>
</g>
<!-- uProcess&#45;&gt;sOutq -->
<g id="edge15" class="edge">
<title>uProcess&#45;&gt;sOutq</title>
<path fill="none" stroke="#0000ff" d="M792.2713,-642.203C787.8428,-596.3198 779.1418,-506.1699 774.7193,-460.3495"/>
<polygon fill="#0000ff" stroke="#0000ff" points="773.7426,-450.2297 779.1826,-459.7511 774.223,-455.2066 774.7034,-460.1835 774.7034,-460.1835 774.7034,-460.1835 774.223,-455.2066 770.2242,-460.6158 773.7426,-450.2297 773.7426,-450.2297"/>
</g>
<!-- uProcess&#45;&gt;userState -->
<g id="edge20" class="edge">
<title>uProcess&#45;&gt;userState</title>
<path fill="none" stroke="#ff8888" d="M804.7422,-632.4164C814.4107,-594.6461 829.3748,-536.188 840.6433,-492.1666"/>
<polygon fill="#ff8888" stroke="#ff8888" points="802.237,-642.203 800.3575,-631.3994 803.477,-637.3592 804.717,-632.5154 804.717,-632.5154 804.717,-632.5154 803.477,-637.3592 809.0764,-633.6313 802.237,-642.203 802.237,-642.203"/>
<polygon fill="#ff8888" stroke="#ff8888" points="843.1328,-482.4415 845.0123,-493.2451 841.8928,-487.2854 840.6529,-492.1292 840.6529,-492.1292 840.6529,-492.1292 841.8928,-487.2854 836.2934,-491.0132 843.1328,-482.4415 843.1328,-482.4415"/>
</g>
<!-- uRespq -->
<g id="node13" class="node">
<title>uRespq</title>
<polygon fill="none" stroke="#000000" points="744.5723,-57.4017 699.4277,-57.4017 699.4277,-22.1983 744.5723,-22.1983 744.5723,-16.1983 762.5723,-39.8 744.5723,-63.4017 744.5723,-57.4017"/>
<text text-anchor="middle" x="731" y="-49.7" font-family="arial" font-size="11.00" fill="#000000">user</text>
<text text-anchor="middle" x="731" y="-36.5" font-family="arial" font-size="11.00" fill="#000000">SMP</text>
<text text-anchor="middle" x="731" y="-23.3" font-family="arial" font-size="11.00" fill="#000000">TBQueue</text>
</g>
<!-- uRespq&#45;&gt;uProcess -->
<g id="edge29" class="edge">
<title>uRespq&#45;&gt;uProcess</title>
<path fill="none" stroke="#000000" d="M762.5896,-44.0035C813.9769,-51.7359 910,-70.3048 910,-101.1 910,-553.4624 910,-553.4624 910,-553.4624 910,-594.8589 872.935,-626.9659 840.89,-646.9888"/>
<polygon fill="#000000" stroke="#000000" points="832.0197,-652.3135 838.2775,-643.3085 836.3067,-649.7401 840.5936,-647.1667 840.5936,-647.1667 840.5936,-647.1667 836.3067,-649.7401 842.9097,-651.0249 832.0197,-652.3135 832.0197,-652.3135"/>
</g>
<!-- sAgent -->
<g id="node17" class="node">
<title>sAgent</title>
<polygon fill="none" stroke="#ffa500" points="656.0134,-172.6875 635.0067,-200.8627 592.9933,-200.8627 571.9866,-172.6875 592.9933,-144.5122 635.0067,-144.5122 656.0134,-172.6875"/>
<text text-anchor="middle" x="614" y="-175.9875" font-family="arial" font-size="11.00" fill="#000000">server</text>
<text text-anchor="middle" x="614" y="-162.7875" font-family="arial" font-size="11.00" fill="#000000">receive</text>
</g>
<!-- runClient&#45;&gt;sAgent -->
<g id="edge12" class="edge">
<title>runClient&#45;&gt;sAgent</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M663.5617,-414.1714C650.0082,-397.4105 633,-371.0774 633,-344.475 633,-344.475 633,-344.475 633,-257.475 633,-242.0587 629.9721,-225.439 626.3339,-211.0334"/>
<polygon fill="#ffa500" stroke="#ffa500" points="623.6113,-200.9841 630.5697,-209.4594 624.9188,-205.8101 626.2263,-210.6361 626.2263,-210.6361 626.2263,-210.6361 624.9188,-205.8101 621.8829,-211.8129 623.6113,-200.9841 623.6113,-200.9841"/>
<text text-anchor="middle" x="642.7235" y="-297.975" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- sSnd -->
<g id="node18" class="node">
<title>sSnd</title>
<polygon fill="none" stroke="#ffa500" points="750.0217,-172.6875 731.0109,-200.8627 692.9891,-200.8627 673.9783,-172.6875 692.9891,-144.5122 731.0109,-144.5122 750.0217,-172.6875"/>
<text text-anchor="middle" x="712" y="-175.9875" font-family="arial" font-size="11.00" fill="#000000">server</text>
<text text-anchor="middle" x="712" y="-162.7875" font-family="arial" font-size="11.00" fill="#000000">send</text>
</g>
<!-- runClient&#45;&gt;sSnd -->
<g id="edge13" class="edge">
<title>runClient&#45;&gt;sSnd</title>
<path fill="none" stroke="#ffa500" stroke-dasharray="5,2" d="M684.2567,-414.1504C688.048,-396.5013 693,-368.7944 693,-344.475 693,-344.475 693,-344.475 693,-257.475 693,-242.0587 696.0279,-225.439 699.6661,-211.0334"/>
<polygon fill="#ffa500" stroke="#ffa500" points="702.3887,-200.9841 704.1171,-211.8129 701.0812,-205.8101 699.7737,-210.6361 699.7737,-210.6361 699.7737,-210.6361 701.0812,-205.8101 695.4303,-209.4594 702.3887,-200.9841 702.3887,-200.9841"/>
<text text-anchor="middle" x="702.7235" y="-297.975" font-family="arial" font-size="10.00" fill="#ffa500">race</text>
</g>
<!-- sOutq&#45;&gt;sSnd -->
<g id="edge16" class="edge">
<title>sOutq&#45;&gt;sSnd</title>
<path fill="none" stroke="#0000ff" d="M767.4159,-414.1688C763.3329,-396.5332 758,-368.8362 758,-344.475 758,-344.475 758,-344.475 758,-257.475 758,-238.8558 749.3238,-220.4171 739.5075,-205.5139"/>
<polygon fill="#0000ff" stroke="#0000ff" points="733.653,-197.1731 743.0814,-202.7728 736.5256,-201.2656 739.3981,-205.3581 739.3981,-205.3581 739.3981,-205.3581 736.5256,-201.2656 735.7149,-207.9434 733.653,-197.1731 733.653,-197.1731"/>
</g>
<!-- sSock -->
<g id="node16" class="node">
<title>sSock</title>
<polygon fill="none" stroke="#0000ff" points="619.2006,-450.175 434.7994,-450.175 434.7994,-414.175 619.2006,-414.175 619.2006,-450.175"/>
<text text-anchor="middle" x="527" y="-428.875" font-family="arial" font-size="11.00" fill="#000000">SMP client connection TCP socket</text>
</g>
<!-- sSock&#45;&gt;sAgent -->
<g id="edge18" class="edge">
<title>sSock&#45;&gt;sAgent</title>
<path fill="none" stroke="#0000ff" d="M533.3267,-413.9457C538.8538,-396.3776 546,-368.9339 546,-344.475 546,-344.475 546,-344.475 546,-257.475 546,-234.2863 561.6699,-213.8223 577.9653,-198.8106"/>
<polygon fill="#0000ff" stroke="#0000ff" points="585.9492,-191.8912 581.3395,-201.8412 582.1708,-195.1659 578.3923,-198.4406 578.3923,-198.4406 578.3923,-198.4406 582.1708,-195.1659 575.4451,-195.04 585.9492,-191.8912 585.9492,-191.8912"/>
</g>
<!-- sAgent&#45;&gt;uRespq -->
<g id="edge28" class="edge">
<title>sAgent&#45;&gt;uRespq</title>
<path fill="none" stroke="#000000" d="M636.8365,-146.75C655.8728,-125.1288 683.0563,-94.254 703.3269,-71.2308"/>
<polygon fill="#000000" stroke="#000000" points="710.1839,-63.4427 706.9532,-73.9219 706.8798,-67.1955 703.5757,-70.9482 703.5757,-70.9482 703.5757,-70.9482 706.8798,-67.1955 700.1982,-67.9746 710.1839,-63.4427 710.1839,-63.4427"/>
</g>
<!-- sSnd&#45;&gt;sSock -->
<g id="edge17" class="edge">
<title>sSnd&#45;&gt;sSock</title>
<path fill="none" stroke="#0000ff" d="M693.7955,-200.8992C685.9738,-211.2174 676.0767,-222.2439 665,-229.975 634.5137,-251.2531 586,-220.2974 586,-257.475 586,-344.475 586,-344.475 586,-344.475 586,-368.2876 570.8403,-390.4932 555.8897,-406.6024"/>
<polygon fill="#0000ff" stroke="#0000ff" points="548.6344,-414.0012 552.4229,-403.7105 552.1352,-410.4312 555.6359,-406.8612 555.6359,-406.8612 555.6359,-406.8612 552.1352,-410.4312 558.8489,-410.0118 548.6344,-414.0012 548.6344,-414.0012"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 31 KiB

+30
View File
@@ -0,0 +1,30 @@
indentation: 2
column-limit: none
function-arrows: trailing
comma-style: trailing
import-export-style: trailing
indent-wheres: true
record-brace-space: true
newlines-between-decls: 1
haddock-style: single-line
haddock-style-module: null
let-style: inline
in-style: right-align
single-constraint-parens: never
unicode: never
respectful: true
fixities:
- infixr 9 .
- infixr 8 .:, .:., .=
- infixr 6 <>
- infixr 5 ++
- infixl 4 <$>, <$, $>, <$$>, <$?>
- infixl 4 <*>, <*, *>, <**>
- infix 4 ==, /=
- infixr 3 &&
- infixl 3 <|>
- infixr 2 ||
- infixl 1 >>, >>=
- infixr 1 =<<, >=>, <=<
- infixr 0 $, $!
reexports: []
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 506.94 131.5"><path d="m109.65 58.21-18.21-10.07-15.37 9.38-.19 9.59-7.47-4.92-10.16 6.19-.44-10.48-10.48-7 10-5.17c-.08 0 0 1-1.48-34.34l-23.65-11.39-32.2 10 7.19 34.63 10.81 8.37-8.22 3.9 5.37 26.1 7.55 7.09-5.4 3.29 4.19 20.18 16.94 17.94c.08-.1 1.78-1.41 21.79-17.32l-.58-13.71 8.58 7.28c.12-.12 1.84-1.39 17.56-13.89l.61-10.14 6.48 4.5c.13-.12 1.58-1.22 14.26-11.33z" fill="#231f20"/><path d="m94.54 69 15.11-10.79-18.21-10.07-15.37 9.38z"/><path d="m92.87 88.2 1.67-19.2-18.47-11.48-.38 18.75z" fill="#004b16"/><path d="m68.22 107.73v-19.89l-18.66-14.15 1.47 19.54z" fill="#004b16"/><path d="m68.22 87.84 18.74-13.43-18.55-12.22-18.85 11.5z"/><path d="m38.43 131.48-2.98-20.32-18.15-17.8 4.19 20.18z" fill="#004b16"/><path d="m35.45 111.16 23.91-17.06-18.53-15.09-23.53 14.35z"/><path d="m33.9 100.6-3.94-26.88-20.22-16.81 5.41 26.07z" fill="#004b16"/><path d="m29.96 73.72 27.85-15.82-20.8-13.94-27.27 12.95z"/><path d="m28.07 60.88-5.4-36.81-22.67-14.06 7.19 34.62z" fill="#004b16"/><path d="m22.67 24.07 33.14-12.7-23.61-11.37-32.2 10.01z"/><g fill="#1cb35c"><path d="m107.13 76.87c-14.02 11.13-14.33 11.33-14.26 11.33 1.74-20.1 1.59-19.2 1.67-19.2 16-11.45 15-10.79 15.11-10.79z"/><path d="m85.78 93.84c-17.35 13.8-17.63 13.89-17.56 13.89-.17-20.82-.07-19.89 0-19.89 20-14.3 18.67-13.43 18.74-13.43z"/><path d="m60.22 114.16c-21.66 17.22-21.86 17.32-21.79 17.32-3.07-20.94-3-20.32-3-20.32 25.47-18.16 23.86-17.06 23.93-17.06z"/><path d="m55.81 11.37c1.52 35.37 1.4 34.34 1.48 34.34-28.66 14.89-29.29 15.17-29.22 15.17-5.52-37.63-5.47-36.81-5.4-36.81z"/><path d="m57.81 57.9c1.15 26.81 1 25.88 1.11 25.88-24.81 16.67-25.09 16.82-25 16.82-4-27.58-4-26.88-3.94-26.88z"/></g><path xmlns="http://www.w3.org/2000/svg" d="m151.61 14.24 16.58-4v79.88q0 13.13 7.83 15.65-3.84 7.31-13.13 7.3-11.28 0-11.28-15.66z"/><path d="m186.66 111.72v-57.42h-9.08v-13.6h25.86v71zm8.56-98.54a9.62 9.62 0 1 1 -9.62 9.62 9.63 9.63 0 0 1 9.62-9.62z"/><path d="m262.94 111.74v-41.06c0-6.05-1.16-10.48-3.48-13.26s-6.11-4.18-11.37-4.18a17.74 17.74 0 0 0 -7.8 2.06 18 18 0 0 0 -6.46 5.1v51.34h-16.59v-71h11.94l3 6.64q6.76-8 20-8 12.66 0 20 7.59t7.33 21.19v43.58z"/><path d="m288.42 76.06q0-16.26 9.38-26.47t24.77-10.21q16.19 0 25.14 9.82t9 26.86q0 17-9.12 27t-25 10q-16.18 0-25.17-10.12t-9-26.88zm17.24 0q0 23.47 16.91 23.48a14.54 14.54 0 0 0 12.31-6.11q4.55-6.09 4.54-17.37 0-23.14-16.85-23.15a14.61 14.61 0 0 0 -12.33 6.09q-4.58 6.11-4.58 17.06z"/><path d="m412.37 111.74v-4.31c-1.37 1.5-3.71 2.82-7 3.95a31.33 31.33 0 0 1 -10.15 1.69q-14.87 0-23.38-9.42t-8.52-26.27q0-16.84 9.78-27.42a32 32 0 0 1 24.51-10.58 32.37 32.37 0 0 1 14.72 3.32v-28.46l16.58-4v101.5zm0-54.06a17.6 17.6 0 0 0 -11.07-4.24q-10 0-15.33 6.07t-5.37 17.41q0 22.15 21.36 22.15a16.11 16.11 0 0 0 5.87-1.42c2.32-1 3.83-1.92 4.54-2.89z"/><path d="m505.55 81.3h-50.74q.46 8.49 5.83 13.2t14.46 4.7q11.34 0 17.25-5.9l6.43 12.7q-8.75 7.09-26.13 7.1-16.25 0-25.7-9.52t-9.45-26.58q0-16.78 10.38-27.2a33.91 33.91 0 0 1 24.9-10.41q15.45 0 24.81 9.22t9.35 23.48a46.28 46.28 0 0 1 -1.39 9.21zm-50.15-12.47h34.89q-1.73-15.58-17.24-15.59-14.2 0-17.65 15.59z"/></svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

Executable
+228
View File
@@ -0,0 +1,228 @@
#!/usr/bin/env sh
set -eu
# Links to scripts/configs
scripts="https://raw.githubusercontent.com/simplex-chat/simplexmq/stable/scripts/main"
scripts_systemd_smp="$scripts/smp-server.service"
scripts_systemd_xftp="$scripts/xftp-server.service"
scripts_update="$scripts/simplex-servers-update"
scripts_uninstall="$scripts/simplex-servers-uninstall"
scripts_stopscript="$scripts/simplex-servers-stopscript"
# Default installation paths
path_bin="/usr/local/bin"
path_bin_smp="$path_bin/smp-server"
path_bin_xftp="$path_bin/xftp-server"
path_bin_update="$path_bin/simplex-servers-update"
path_bin_uninstall="$path_bin/simplex-servers-uninstall"
path_bin_stopscript="$path_bin/simplex-servers-stopscript"
path_conf_etc="/etc/opt"
path_conf_var="/var/opt"
path_conf_smp="$path_conf_etc/simplex $path_conf_var/simplex"
path_conf_xftp="$path_conf_etc/simplex-xftp $path_conf_var/simplex-xftp /srv/xftp"
path_conf_info="$path_conf_etc/simplex-info"
path_systemd="/etc/systemd/system"
path_systemd_smp="$path_systemd/smp-server.service"
path_systemd_xftp="$path_systemd/xftp-server.service"
# Defaut users
user_smp="smp"
user_xftp="xftp"
GRN='\033[0;32m'
BLU='\033[1;34m'
YLW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
logo='
____ _ _ __ __
/ ___|(_)_ __ ___ _ __ | | ___\ \/ /
\___ \| | '"'"'_ ` _ \| '"'"'_ \| |/ _ \\ /
___) | | | | | | | |_) | | __// \
|____/|_|_| |_| |_| .__/|_|\___/_/\_\
|_|
'
welcome="Welcome to SMP/XFTP installation script! Here's what we're going to do:
${GRN}1.${NC} Install latest binaries from GitHub releases:
- smp: ${YLW}${path_bin_smp}${NC}
- xftp: ${YLW}${path_bin_xftp}${NC}
${GRN}2.${NC} Create server directories:
- smp: ${YLW}${path_conf_smp}${NC}
- xftp: ${YLW}${path_conf_xftp}${NC}
${GRN}3.${NC} Setup user for server:
- xmp: ${YLW}${user_smp}${NC}
- xftp: ${YLW}${user_xftp}${NC}
${GRN}4.${NC} Create systemd services:
- smp: ${YLW}${path_systemd_smp}${NC}
- xftp: ${YLW}${path_systemd_xftp}${NC}
${GRN}5.${NC} Install stopscript (systemd), update and uninstallation script:
- all: ${YLW}${path_bin_update}${NC}, ${YLW}${path_bin_uninstall}${NC}, ${YLW}${path_bin_stopscript}${NC}
Press:
- ${GRN}1${NC} to install smp server
- ${GRN}2${NC} to install xftp server
- ${RED}Ctrl+C${NC} to cancel installation
Selection: "
end="Installtion is complete!
Please checkout our server guides:
- smp: ${GRN}https://simplex.chat/docs/server.html${NC}
- xftp: ${GRN}https://simplex.chat/docs/xftp-server.html${NC}
To uninstall with full clean-up, simply run: ${YLW}sudo /usr/local/bin/simplex-servers-uninstall${NC}
"
set_version() {
ver="${VER:-latest}"
case "$ver" in
latest)
bin="https://github.com/simplex-chat/simplexmq/releases/latest/download"
remote_version="$(curl --proto '=https' --tlsv1.2 -sSf -L https://api.github.com/repos/simplex-chat/simplexmq/releases/latest | grep -i "tag_name" | awk -F \" '{print $4}')"
;;
*)
bin="https://github.com/simplex-chat/simplexmq/releases/download/${ver}"
remote_version="${ver}"
;;
esac
}
os_test() {
. /etc/os-release
case "$VERSION_ID" in
20.04|22.04) : ;;
24.04) VERSION_ID='22.04' ;;
*) printf "${RED}Unsupported Ubuntu version!${NC}\nPlease file Github issue with request to support Ubuntu %s: https://github.com/simplex-chat/simplexmq/issues/new\n" "$VERSION_ID" && exit 1 ;;
esac
version="$(printf '%s' "$VERSION_ID" | tr '.' '_')"
arch="$(uname -p)"
case "$arch" in
x86_64) arch="$(printf '%s' "$arch" | tr '_' '-')" ;;
*) printf "${RED}Unsupported architecture!${NC}\nPlease file Github issue with request to support %s architecture: https://github.com/simplex-chat/simplexmq/issues/new" "$arch" && exit 1 ;;
esac
bin_smp="$bin/smp-server-ubuntu-${version}-${arch}"
bin_xftp="$bin/xftp-server-ubuntu-${version}-${arch}"
}
setup_bins() {
eval "bin=\$bin_${1}"
eval "path=\$path_bin_${1}"
curl --proto '=https' --tlsv1.2 -sSf -L "$bin" -o "$path" && chmod +x "$path"
unset bin path
}
setup_users() {
eval "user=\$user_${1}"
useradd -M "$user" 2> /dev/null || true
unset user
}
setup_dirs() {
# Unquoted varibles, so field splitting can occur
eval "path_conf=\$path_conf_${1}"
eval "user=\$user_${1}"
mkdir -p $path_conf
mkdir -p $path_conf_info
printf "local_version_%s='%s'\n" "$1" "$remote_version" >> "$path_conf_info/release"
chown -R "$user":"$user" $path_conf
unset path_conf user
}
setup_systemd() {
eval "scripts_systemd=\$scripts_systemd_${1}"
eval "path_systemd=\$path_systemd_${1}"
curl --proto '=https' --tlsv1.2 -sSf -L "$scripts_systemd" -o "$path_systemd"
unset scripts_systemd path_systemd
}
setup_scripts() {
curl --proto '=https' --tlsv1.2 -sSf -L "$scripts_update" -o "$path_bin_update" && chmod +x "$path_bin_update"
curl --proto '=https' --tlsv1.2 -sSf -L "$scripts_uninstall" -o "$path_bin_uninstall" && chmod +x "$path_bin_uninstall"
curl --proto '=https' --tlsv1.2 -sSf -L "$scripts_stopscript" -o "$path_bin_stopscript" && chmod +x "$path_bin_stopscript"
}
checks() {
if [ "$(id -u)" -ne 0 ]; then
printf "This script is intended to be run with root privileges. Please re-run script using sudo."
exit 1
fi
set_version
os_test
mkdir -p $path_conf_info
}
main() {
checks
printf "%b\n%b" "${BLU}$logo${NC}" "$welcome"
read ans
case "$ans" in
1) setup='smp' ;;
2) setup='xftp' ;;
*) printf 'Installation aborted.\n' && exit 0 ;;
esac
printf "Installing binaries..."
for i in $setup; do
setup_bins "$i"
done
printf "${GRN} Done!${NC}\n"
printf "Creating users..."
for i in $setup; do
setup_users "$i"
done
printf "${GRN} Done!${NC}\n"
printf "Creating directories..."
for i in $setup; do
setup_dirs "$i"
done
printf "${GRN} Done!${NC}\n"
printf "Creating systemd services..."
for i in $setup; do
setup_systemd "$i"
done
printf "${GRN} Done!${NC}\n"
printf "Installing stopscript, update and uninstallation script..."
setup_scripts
printf "${GRN} Done!${NC}\n"
printf "%b" "$end"
}
main
-58
View File
@@ -1,58 +0,0 @@
name: simplex-messaging
version: 0.1.0.0
#synopsis:
#description:
homepage: https://github.com/simplex-chat/simplex-messaging#readme
license: AGPL-3
author: Evgeny Poberezkin
maintainer: evgeny@poberezkin.com
copyright: 2020 Evgeny Poberezkin
category: Web, System, Services, Cryptography
extra-source-files:
- README.md
dependencies:
- base >= 4.7 && < 5
- base64-bytestring >= 1.0 && < 1.3
- bytestring == 0.10.*
- containers
- cryptonite == 0.26.*
- iso8601-time == 0.1.*
- mtl
- network == 3.1.*
- stm
- time == 1.9.*
- unliftio == 0.2.*
- unliftio-core == 0.1.*
library:
source-dirs: src
executables:
smp-server:
source-dirs: apps/smp-server
main: Main.hs
dependencies:
- simplex-messaging
ghc-options:
- -threaded
tests:
smp-server-test:
source-dirs: tests
main: Test.hs
dependencies:
- simplex-messaging
- hspec == 2.7.*
- hspec-core == 2.7.*
- HUnit == 1.6.*
ghc-options:
# - -haddock
- -Wall
- -Wcompat
- -Werror=incomplete-patterns
- -Wredundant-constraints
- -Wincomplete-record-updates
- -Wincomplete-uni-patterns
- -Wunused-type-patterns
@@ -0,0 +1,472 @@
# XFTP Server PostgreSQL Backend
## Overview
Add PostgreSQL backend support to xftp-server, following the SMP server pattern. Supports bidirectional migration between STM (in-memory with StoreLog) and PostgreSQL backends.
## Goals
- PostgreSQL-backed file metadata storage as an alternative to STM + StoreLog
- Polymorphic server code via `FileStoreClass` typeclass with IO-based methods (following `QueueStoreClass` pattern)
- Bidirectional migration: StoreLog <-> PostgreSQL via CLI commands
- Shared `server_postgres` cabal flag (same flag enables both SMP and XFTP Postgres support)
- INI-based backend selection at runtime
## Architecture
### FileStoreClass Typeclass
IO-based typeclass following the `QueueStoreClass` pattern — each method is a self-contained IO action, with the implementation responsible for its own atomicity (STM backend wraps in `atomically`, Postgres backend uses database transactions):
```haskell
class FileStoreClass s where
type FileStoreConfig s
-- Lifecycle
newFileStore :: FileStoreConfig s -> IO s
closeFileStore :: s -> IO ()
-- File operations
addFile :: s -> SenderId -> FileInfo -> RoundedFileTime -> ServerEntityStatus -> IO (Either XFTPErrorType ())
setFilePath :: s -> SenderId -> FilePath -> IO (Either XFTPErrorType ())
addRecipient :: s -> SenderId -> FileRecipient -> IO (Either XFTPErrorType ())
getFile :: s -> SFileParty p -> XFTPFileId -> IO (Either XFTPErrorType (FileRec, C.APublicAuthKey))
deleteFile :: s -> SenderId -> IO (Either XFTPErrorType ())
blockFile :: s -> SenderId -> BlockingInfo -> Bool -> IO (Either XFTPErrorType ())
deleteRecipient :: s -> RecipientId -> FileRec -> IO ()
ackFile :: s -> RecipientId -> IO (Either XFTPErrorType ())
-- Expiration (with LIMIT for Postgres; called in a loop until empty)
expiredFiles :: s -> Int64 -> Int -> IO [(SenderId, Maybe FilePath, Word32)]
-- Storage and stats (for init-time computation)
getUsedStorage :: s -> IO Int64
getFileCount :: s -> IO Int
```
- STM backend: each method wraps its STM transaction in `atomically` internally.
- Postgres backend: each method runs its query via `withDB` / database connection internally.
No polymorphic monad or `runStore` dispatcher needed — unlike `MsgStoreClass`, XFTP file operations are individually atomic and don't require grouping multiple operations into backend-dependent transactions.
### PostgresFileStore Data Type
```haskell
data PostgresFileStore = PostgresFileStore
{ dbStore :: DBStore,
dbStoreLog :: Maybe (StoreLog 'WriteMode)
}
```
- `dbStore` — connection pool created via `createDBStore`, runs schema migrations on init.
- `dbStoreLog` — optional parallel log file (enabled by `db_store_log` INI setting). When present, every mutation (`addFile`, `setFilePath`, `deleteFile`, `blockFile`, `addRecipient`, `ackFile`) also writes to this log via a `withLog` wrapper. `withLog` is called AFTER the DB operation succeeds (so the log reflects committed state only). Log write failures are non-fatal (logged as warnings, do not fail the DB operation). This provides an audit trail and enables recovery via export.
`closeFileStore` for Postgres calls `closeDBStore` (closes connection pool) then `mapM_ closeStoreLog dbStoreLog` (flushes and closes the parallel log). For STM, it closes the storeLog. Called from a `finally` block during server shutdown, matching SMP's `stopServer``closeMsgStore``closeQueueStore` pattern.
### STMFileStore Type
After extracting from current `Store.hs`, `STMFileStore` retains the file and recipient maps but no longer owns `usedStorage` (moved to `XFTPEnv`):
```haskell
data STMFileStore = STMFileStore
{ files :: TMap SenderId FileRec,
recipients :: TMap RecipientId (SenderId, RcvPublicAuthKey)
}
```
`closeFileStore` for STM is a no-op (TMaps are garbage-collected; the env-level `storeLog` is closed separately by the server).
### Error Handling
Postgres operations follow SMP's `withDB` / `handleDuplicate` pattern:
```haskell
withDB :: Text -> PostgresFileStore -> (DB.Connection -> IO (Either XFTPErrorType a)) -> ExceptT XFTPErrorType IO a
withDB op st action =
ExceptT $ E.try (withTransaction (dbStore st) action) >>= either logErr pure
where
logErr :: E.SomeException -> IO (Either XFTPErrorType a)
logErr e = logError ("STORE: " <> err) $> Left INTERNAL
where
err = op <> ", withDB, " <> tshow e
handleDuplicate :: SqlError -> IO (Either XFTPErrorType a)
handleDuplicate e = case constraintViolation e of
Just (UniqueViolation _) -> pure $ Left DUPLICATE_
_ -> E.throwIO e
```
- All DB operations wrapped in `withDB` — catches exceptions, logs, returns `INTERNAL`.
- Unique constraint violations caught by `handleDuplicate` and mapped to `DUPLICATE_`.
- UPDATE operations verified with `assertUpdated` — returns `AUTH` if 0 rows affected (matching SMP pattern, prevents silent failures when WHERE clause doesn't match).
- Critical sections (DB write + TVar update) wrapped in `uninterruptibleMask_` to prevent async exceptions from leaving inconsistent state between DB and TVars.
### FileRec and TVar Fields
`FileRec` retains its `TVar` fields (matching SMP's `PostgresQueue` pattern):
```haskell
data FileRec = FileRec
{ senderId :: SenderId,
fileInfo :: FileInfo,
filePath :: TVar (Maybe FilePath),
recipientIds :: TVar (Set RecipientId),
createdAt :: RoundedFileTime,
fileStatus :: TVar ServerEntityStatus
}
```
- **STM backend**: TVars are the source of truth, as currently.
- **Postgres backend**: `getFile` reads from DB and creates a `FileRec` with fresh TVars populated from the DB row (matching SMP's `mkQ` pattern — `newTVarIO` per load). Mutation methods (`setFilePath`, `blockFile`, etc.) update both the DB (persistence) and the TVars (in-session consistency). The `recipientIds` TVar is initialized to `S.empty` — no subquery needed because no server code reads `recipientIds` directly; all recipient operations go through the typeclass methods (`addRecipient`, `deleteRecipient`, `ackFile`), which query the `recipients` table for Postgres.
### usedStorage Ownership
`usedStorage :: TVar Int64` moves from the store to `XFTPEnv`. The store typeclass does **not** manage `usedStorage` — it only provides `getUsedStorage` for init-time computation.
- **STM init**: StoreLog replay calls `setFilePath` (which only sets the filePath TVar — the STM `setFilePath` implementation is changed to **not** update `usedStorage`). Similarly, STM `deleteFile` (Store.hs line 117) and `blockFile` (line 125) are changed to **not** update `usedStorage` — the server handles all `usedStorage` adjustments externally. After replay, `getUsedStorage` computes the sum over all file sizes (matching current `countUsedStorage` behavior).
- **Postgres init**: `getUsedStorage` executes `SELECT COALESCE(SUM(file_size), 0) FROM files`.
- **Runtime**: Server manages `usedStorage` TVar directly for reserve/commit/rollback during uploads, and adjusts after `deleteFile`/`blockFile` calls.
**Note on `getUsedStorage` semantics**: The current STM `countUsedStorage` sums all file sizes unconditionally (including files without `filePath` set, i.e., created but not yet uploaded). The Postgres `getUsedStorage` matches this: `SELECT SUM(file_size) FROM files` (no `WHERE file_path IS NOT NULL`). In practice, orphaned files (created but never uploaded) are rare and short-lived (expired within 48h), so the difference is negligible. A future improvement could filter by `file_path IS NOT NULL` in both backends to reflect actual disk usage more accurately.
### Server.hs Refactoring
`Server.hs` becomes polymorphic over `FileStoreClass s`. Since all typeclass methods are IO, call sites replace `atomically` with direct IO calls to the store.
**Call sites requiring changes** (exhaustive list):
1. **`receiveServerFile`** (line 563): `atomically $ writeTVar filePath (Just fPath)``setFilePath store senderId fPath`. The `reserve` logic (line 551-555) stays as direct TVar manipulation on `usedStorage` from `XFTPEnv`.
2. **`verifyXFTPTransmission`** (line 453): `atomically $ verify =<< getFile st party fId` — the `getFile` call and subsequent `readTVar fileStatus` are in a single `atomically` block. Refactored to: `getFile st party fId` (IO), then `readTVarIO (fileStatus fr)` from the returned `FileRec` (safe for both backends — STM TVar is the source of truth, Postgres TVar is a fresh snapshot from DB).
3. **`retryAdd`** (line 516): Signature `XFTPFileId -> STM (Either XFTPErrorType a)``XFTPFileId -> IO (Either XFTPErrorType a)`. The `atomically` call (line 520) replaced with `liftIO`.
4. **`deleteOrBlockServerFile_`** (line 620): Parameter `FileStore -> STM (Either XFTPErrorType ())``FileStoreClass s => s -> IO (Either XFTPErrorType ())`. The `atomically` call (line 626) removed — the store method is already IO. After the store action, server adjusts `usedStorage` TVar in `XFTPEnv` based on `fileInfo.size`.
5. **`ackFileReception`** (line 605): `atomically $ deleteRecipient st rId fr``deleteRecipient st rId fr`.
6. **Control port `CPDelete`/`CPBlock`** (lines 371, 377): `atomically $ getFile fs SFRecipient fileId``getFile fs SFRecipient fileId`.
7. **`expireServerFiles`** (line 636): Replace per-file `expiredFilePath` iteration with batched `expiredFiles st old batchSize`, which returns `[(SenderId, Maybe FilePath, Word32)]` — the `Word32` file size is needed so the server can adjust the `usedStorage` TVar after each deletion. Called in a loop until the returned list is empty. The `itemDelay` between files applies to the deletion loop over each batch, not the query itself. STM backend ignores the batch size limit (returns all expired files from TMap scan); Postgres uses `LIMIT`.
8. **`restoreServerStats`** (line 694): `FileStore {files, usedStorage} <- asks store` accesses store fields directly. Refactored to: `usedStorage` from `XFTPEnv` via `asks usedStorage`, file count via `getFileCount store`. STM: `M.size <$> readTVarIO files`. Postgres: `SELECT COUNT(*) FROM files`.
### Store Config Selection
GADT in `Env.hs`:
```haskell
data XFTPStoreConfig s where
XSCMemory :: Maybe FilePath -> XFTPStoreConfig STMFileStore
#if defined(dbServerPostgres)
XSCDatabase :: PostgresFileStoreCfg -> XFTPStoreConfig PostgresFileStore
#endif
```
`XFTPEnv` becomes polymorphic:
```haskell
data XFTPEnv s = XFTPEnv
{ config :: XFTPServerConfig,
store :: s,
usedStorage :: TVar Int64,
storeLog :: Maybe (StoreLog 'WriteMode),
...
}
```
The `M` monad (`ReaderT (XFTPEnv s) IO`) and all functions in `Server.hs` gain `FileStoreClass s =>` constraints.
**StoreLog lifecycle per backend:**
- **STM mode**: `storeLog = Just sl` (current behavior — append-only log for persistence and recovery).
- **Postgres mode**: `storeLog = Nothing` (main storeLog disabled — Postgres is the source of truth). The optional parallel `dbStoreLog` inside `PostgresFileStore` provides audit/recovery if enabled via `db_store_log` INI setting.
The existing `withFileLog` pattern in Server.hs continues to work unchanged — it maps over `Maybe (StoreLog 'WriteMode)`, which is `Nothing` in Postgres mode so the calls become no-ops.
### Main.hs Store Type Dispatch
The `Start` CLI command gains a `--confirm-migrations` flag (default `MCConsole` — manual prompt, matching SMP's `StartOptions`). For automated deployments, `--confirm-migrations up` auto-applies forward migrations. The import command uses `MCYesUp` (always auto-apply).
Following SMP's existential dispatch pattern (`AStoreType` + `run`), `Main.hs` selects the store type from INI config and dispatches to the polymorphic server:
```haskell
runServer ini = do
let storeType = fromRight "memory" $ lookupValue "STORE_LOG" "store_files" ini
case storeType of
"memory" -> run $ XSCMemory (enableStoreLog $> storeLogFilePath)
"database" ->
#if defined(dbServerPostgres)
run $ XSCDatabase PostgresFileStoreCfg {..}
#else
exitError "server not compiled with Postgres support"
#endif
_ -> exitError $ "Invalid store_files value: " <> storeType
where
run :: FileStoreClass s => XFTPStoreConfig s -> IO ()
run storeCfg = do
env <- newXFTPServerEnv storeCfg config
runReaderT (xftpServer config) env
```
**`newXFTPServerEnv` refactored signature:**
```haskell
newXFTPServerEnv :: FileStoreClass s => XFTPStoreConfig s -> XFTPServerConfig -> IO (XFTPEnv s)
newXFTPServerEnv storeCfg config = do
(store, storeLog) <- case storeCfg of
XSCMemory storeLogPath -> do
st <- newFileStore ()
sl <- mapM (`readWriteFileStore` st) storeLogPath
pure (st, sl)
XSCDatabase dbCfg -> do
st <- newFileStore dbCfg
pure (st, Nothing) -- main storeLog disabled for Postgres
usedStorage <- newTVarIO =<< getUsedStorage store
...
pure XFTPEnv {config, store, usedStorage, storeLog, ...}
```
### Startup Config Validation
Following SMP's `checkMsgStoreMode` pattern, `Main.hs` validates config before starting:
- **`store_files=database` + StoreLog file exists** (without `db_store_log=on`): Error — "StoreLog file present but store_files is `database`. Use `xftp-server database import` to migrate, or set `db_store_log: on`."
- **`store_files=database` + schema doesn't exist**: Error — "Create schema in PostgreSQL or use `xftp-server database import`."
- **`store_files=memory` + Postgres schema exists**: Warning — "Postgres schema exists but store_files is `memory`. Data in Postgres will not be used."
- **Binary compiled without `server_postgres` + `store_files=database`**: Error — "Server not compiled with Postgres support."
## Module Structure
```
src/Simplex/FileTransfer/Server/
Store.hs -- FileStoreClass typeclass + shared types (FileRec, FileRecipient, etc.)
Store/
STM.hs -- STMFileStore (extracted from current Store.hs)
Postgres.hs -- PostgresFileStore [CPP-guarded]
Postgres/
Migrations.hs -- Schema migrations [CPP-guarded]
Config.hs -- PostgresFileStoreCfg [CPP-guarded]
StoreLog.hs -- Unchanged (interchange format for both backends + migration)
Env.hs -- XFTPStoreConfig GADT, polymorphic XFTPEnv
Main.hs -- Store selection, migration CLI commands
Server.hs -- Polymorphic over FileStoreClass
```
## PostgreSQL Schema
Initial migration (`20260325_initial`):
```sql
CREATE TABLE files (
sender_id BYTEA NOT NULL PRIMARY KEY,
file_size INT4 NOT NULL,
file_digest BYTEA NOT NULL,
sender_key BYTEA NOT NULL,
file_path TEXT,
created_at INT8 NOT NULL,
status TEXT NOT NULL DEFAULT 'active'
);
CREATE TABLE recipients (
recipient_id BYTEA NOT NULL PRIMARY KEY,
sender_id BYTEA NOT NULL REFERENCES files ON DELETE CASCADE,
recipient_key BYTEA NOT NULL
);
CREATE INDEX idx_recipients_sender_id ON recipients (sender_id);
CREATE INDEX idx_files_created_at ON files (created_at);
```
- `file_size` is `INT4` matching `Word32` in `FileInfo.size`
- `sender_key` and `recipient_key` stored as `BYTEA` using binary encoding via `C.encodePubKey` / `C.decodePubKey` (matching SMP's `ToField`/`FromField` instances for `APublicAuthKey` — includes algorithm type tag in the binary format)
- `file_path` nullable (set after upload completes via `setFilePath`)
- `ON DELETE CASCADE` for recipients when file is hard-deleted
- `created_at` stores rounded epoch seconds (1-hour precision, `RoundedFileTime`)
- `status` as TEXT via `StrEncoding` (`ServerEntityStatus`: `EntityActive`, `EntityBlocked info`, `EntityOff`)
- Hard deletes (no `deleted_at` column)
- No PL/pgSQL functions needed; `setFilePath` uses `WHERE file_path IS NULL` to prevent duplicate uploads (the `UPDATE` itself acquires a row-level lock)
- `used_storage` computed on startup: `SELECT COALESCE(SUM(file_size), 0) FROM files` (matches STM `countUsedStorage` — all files, see usedStorage Ownership section)
### Migrations Module
Following SMP's `QueueStore/Postgres/Migrations.hs` pattern:
```haskell
module Simplex.FileTransfer.Server.Store.Postgres.Migrations
( xftpServerMigrations,
)
where
import Data.List (sortOn)
import Data.Text (Text)
import Simplex.Messaging.Agent.Store.Shared
import Text.RawString.QQ (r)
xftpSchemaMigrations :: [(String, Text, Maybe Text)]
xftpSchemaMigrations =
[ ("20260325_initial", m20260325_initial, Nothing)
]
xftpServerMigrations :: [Migration]
xftpServerMigrations = sortOn name $ map migration xftpSchemaMigrations
where
migration (name, up, down) = Migration {name, up, down = down}
m20260325_initial :: Text
m20260325_initial =
[r|
CREATE TABLE files (
sender_id BYTEA NOT NULL PRIMARY KEY,
...
);
|]
```
The `Migration` type (from `Simplex.Messaging.Agent.Store.Shared`) has fields `{name :: String, up :: Text, down :: Maybe Text}`. Initial migration has `Nothing` for `down`. Future migrations should include `Just down_migration` for rollback support. Called via `createDBStore dbOpts xftpServerMigrations (MigrationConfig confirmMigrations Nothing)`.
### Postgres Operations
Key query patterns:
- **`addFile`**: `INSERT INTO files (...) VALUES (...)`, return `DUPLICATE_` on unique violation.
- **`setFilePath`**: `UPDATE files SET file_path = ? WHERE sender_id = ? AND file_path IS NULL`, verified with `assertUpdated` (returns `AUTH` if 0 rows affected — file not found or already uploaded). The `WHERE file_path IS NULL` prevents duplicate uploads; the `UPDATE` acquires a row lock implicitly. Only persists the path; `usedStorage` managed by server.
- **`addRecipient`**: `INSERT INTO recipients (...)`, plus check for duplicates. No need for `recipientIds` TVar update — Postgres derives it from the table.
- **`getFile`** (sender): `SELECT ... FROM files WHERE sender_id = ?`, returns auth key from `sender_key` column.
- **`getFile`** (recipient): `SELECT f.*, r.recipient_key FROM recipients r JOIN files f ON ... WHERE r.recipient_id = ?`.
- **`deleteFile`**: `DELETE FROM files WHERE sender_id = ?` (recipients cascade).
- **`blockFile`**: `UPDATE files SET status = ? WHERE sender_id = ?`. When `deleted = True`, the server adjusts `usedStorage` externally (matching current STM behavior where `blockFile` only updates status and storage, not `filePath`).
- **`expiredFiles`**: `SELECT sender_id, file_path, file_size FROM files WHERE created_at + ? < ? LIMIT ?` — batched query replaces per-file iteration, includes `file_size` for `usedStorage` adjustment. Called in a loop until no rows returned.
## INI Configuration
New keys in `[STORE_LOG]` section:
```ini
[STORE_LOG]
enable: on
store_files: memory # memory | database
db_connection: postgresql://xftp@/xftp_server_store
db_schema: xftp_server
db_pool_size: 10
db_store_log: off
expire_files_hours: 48
```
`store_files` selects the backend (`store_files` rather than `store_queues` because XFTP stores files, not queues):
- `memory` -> `XSCMemory` (current behavior)
- `database` -> `XSCDatabase` (requires `server_postgres` build flag)
### INI Template Generation (`xftp-server init`)
The `iniFileContent` function in `Main.hs` must be updated to generate the new keys in the `[STORE_LOG]` section. Following SMP's `iniDbOpts` pattern with `optDisabled'` (prefixes `"# "` when value equals default), Postgres keys are generated commented out by default:
```ini
[STORE_LOG]
enable: on
# File storage mode: `memory` or `database` (PostgreSQL).
store_files: memory
# Database connection settings for PostgreSQL database (`store_files: database`).
# db_connection: postgresql://xftp@/xftp_server_store
# db_schema: xftp_server
# db_pool_size: 10
# Write database changes to store log file
# db_store_log: off
expire_files_hours: 48
```
Reuses `iniDBOptions` from `Simplex.Messaging.Server.CLI` for runtime parsing (falls back to defaults when keys are commented out or missing). `enableDbStoreLog'` pattern (`settingIsOn "STORE_LOG" "db_store_log"`) controls `dbStoreLogPath`.
### PostgresFileStoreCfg
```haskell
data PostgresFileStoreCfg = PostgresFileStoreCfg
{ dbOpts :: DBOpts,
dbStoreLogPath :: Maybe FilePath,
confirmMigrations :: MigrationConfirmation
}
```
No `deletedTTL` (hard deletes).
### Default DB Options
```haskell
defaultXFTPDBOpts :: DBOpts
defaultXFTPDBOpts =
DBOpts
{ connstr = "postgresql://xftp@/xftp_server_store",
schema = "xftp_server",
poolSize = 10,
createSchema = False
}
```
## Migration CLI
Bidirectional migration via StoreLog as interchange format:
```
xftp-server database import [--database DB_CONN] [--schema DB_SCHEMA] [--pool-size N]
xftp-server database export [--database DB_CONN] [--schema DB_SCHEMA] [--pool-size N]
```
No `--table` flag needed (unlike SMP which has queues/messages/all) — XFTP has a single entity type (files + recipients, always migrated together).
CLI options reuse `dbOptsP` parser from `Simplex.Messaging.Server.CLI`.
### Import (StoreLog -> PostgreSQL)
1. Confirm: prompt user with database connection details and StoreLog path
2. Read and replay StoreLog into temporary `STMFileStore`
3. Connect to PostgreSQL, run schema migrations (`createSchema = True`, `confirmMigrations = MCYesUp`)
4. Batch-insert file records into `files` table using PostgreSQL COPY protocol (matching SMP's `batchInsertQueues` pattern for performance). Progress reported every 10k files.
5. Batch-insert recipient records into `recipients` table using COPY protocol
6. Verify counts: `SELECT COUNT(*) FROM files` / `recipients` — warn if mismatch
7. Rename StoreLog to `.bak` (prevents accidental re-import, preserves original for rollback)
8. Report counts
### Export (PostgreSQL -> StoreLog)
1. Confirm: prompt user with database connection details and output path. Fail if output file already exists.
2. Connect to PostgreSQL
3. Open new StoreLog file for writing
4. Fold over all file records, writing per file (in this order, matching existing `writeFileStore`): `AddFile` (with `ServerEntityStatus` — this preserves `EntityBlocked` state), `AddRecipients`, then `PutFile` (if `file_path` is set)
5. Report counts
Note: `AddFile` carries `ServerEntityStatus` which includes `EntityBlocked info`, so blocking state is preserved through export/import without needing separate `BlockFile` log entries.
File data on disk is untouched by migration — only metadata moves between backends.
## Cabal Integration
Shared `server_postgres` flag. New Postgres modules added to existing conditional block:
```cabal
if flag(server_postgres)
cpp-options: -DdbServerPostgres
exposed-modules:
...existing SMP modules...
Simplex.FileTransfer.Server.Store.Postgres
Simplex.FileTransfer.Server.Store.Postgres.Migrations
Simplex.FileTransfer.Server.Store.Postgres.Config
```
CPP guards (`#if defined(dbServerPostgres)`) in:
- `Store.hs` — Postgres `FromField`/`ToField` instances for XFTP-specific types if needed
- `Env.hs``XSCDatabase` constructor
- `Main.hs` — database CLI commands, store selection for `database` mode, Postgres imports
- `Server.hs` — Postgres-specific imports if needed
## Testing
- **Parameterized server tests**: Existing `xftpServerTests` refactored to accept a store type parameter (following SMP's `SpecWith (ASrvTransport, AStoreType)` pattern). The same server tests run against both STM and Postgres backends — STM tests run unconditionally, Postgres tests added under `#if defined(dbServerPostgres)` with `postgressBracket` for database lifecycle (drop → create → test → drop).
- **Unit tests**: `PostgresFileStore` operations — add/get/delete/block/expire, duplicate detection, auth errors
- **Migration round-trip**: STM store → export to StoreLog → import to Postgres → export back → verify StoreLog equality (including blocked file status)
- **Tests location**: in `tests/` alongside existing XFTP tests, guarded by `server_postgres` CPP flag
- **Test database**: PostgreSQL on `localhost:5432`, using a dedicated `xftp_server_test` schema (dropped and recreated per test run via `postgressBracket`, following SMP's test database lifecycle pattern)
- **Test fixtures**: `testXFTPStoreDBOpts :: DBOpts` with `createSchema = True`, `confirmMigrations = MCYesUp`, in `tests/XFTPClient.hs`
@@ -0,0 +1,648 @@
# XFTP PostgreSQL Backend — Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers-extended-cc:subagent-driven-development (if subagents available) or superpowers-extended-cc:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add PostgreSQL backend support to xftp-server as an alternative to STM + StoreLog, with bidirectional migration.
**Architecture:** Introduce `FileStoreClass` typeclass (IO-based, following `QueueStoreClass` pattern). Extract current STM store into `Store/STM.hs`, make `Server.hs` polymorphic, then add `Store/Postgres.hs` behind `server_postgres` CPP flag. `usedStorage` moves from store to `XFTPEnv` so the server manages quota tracking externally.
**Tech Stack:** Haskell, postgresql-simple, STM, fourmolu, cabal with CPP flags
**Design spec:** `plans/2026-03-25-xftp-postgres-backend-design.md`
---
## File Structure
**Existing files modified:**
- `src/Simplex/FileTransfer/Server/Store.hs` — rewritten: becomes typeclass + shared types
- `src/Simplex/FileTransfer/Server/Env.hs` — polymorphic `XFTPEnv s`, `XFTPStoreConfig` GADT
- `src/Simplex/FileTransfer/Server.hs` — polymorphic over `FileStoreClass s`
- `src/Simplex/FileTransfer/Server/StoreLog.hs` — update for IO store functions
- `src/Simplex/FileTransfer/Server/Main.hs` — INI config, dispatch, CLI commands
- `simplexmq.cabal` — new modules
- `tests/XFTPClient.hs` — Postgres test fixtures
- `tests/Test.hs` — Postgres test group
**New files created:**
- `src/Simplex/FileTransfer/Server/Store/STM.hs``STMFileStore` (extracted from current `Store.hs`)
- `src/Simplex/FileTransfer/Server/Store/Postgres.hs``PostgresFileStore` [CPP-guarded]
- `src/Simplex/FileTransfer/Server/Store/Postgres/Config.hs``PostgresFileStoreCfg` [CPP-guarded]
- `src/Simplex/FileTransfer/Server/Store/Postgres/Migrations.hs` — schema SQL [CPP-guarded]
- `tests/CoreTests/XFTPStoreTests.hs` — Postgres store unit tests [CPP-guarded]
---
## Task 1: Move `usedStorage` from `FileStore` to `XFTPEnv`
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Store.hs`
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- Modify: `src/Simplex/FileTransfer/Server.hs`
- [ ] **Step 1: Remove `usedStorage` from `FileStore` in `Store.hs`**
1. Remove `usedStorage :: TVar Int64` field from `FileStore` record (line 47).
2. Remove `usedStorage <- newTVarIO 0` from `newFileStore` (line 75) and drop the field from the record construction (line 76).
3. In `setFilePath` (line 92-97): remove `modifyTVar' (usedStorage st) (+ fromIntegral (size fileInfo))` — keep only `writeTVar filePath (Just fPath)`. Change pattern from `\FileRec {fileInfo, filePath}` to `\FileRec {filePath}` (fileInfo is now unused — `-Wunused-matches` error).
4. In `deleteFile` (line 112-119): remove `modifyTVar' usedStorage $ subtract (fromIntegral $ size fileInfo)`. Change outer pattern match from `FileStore {files, recipients, usedStorage}` to `FileStore {files, recipients}`. Change inner pattern from `Just FileRec {fileInfo, recipientIds}` to `Just FileRec {recipientIds}` (`fileInfo` is now unused — `-Wunused-matches` error).
5. In `blockFile` (line 122-127): remove `when deleted $ modifyTVar' usedStorage $ subtract (fromIntegral $ size fileInfo)`. Change pattern match from `st@FileStore {usedStorage}` to `st`. The `deleted` parameter and `fileInfo` in the inner pattern become unused — prefix with `_` or remove from pattern to avoid `-Wunused-matches`.
- [ ] **Step 2: Add `usedStorage` to `XFTPEnv` in `Env.hs`**
1. Add `usedStorage :: TVar Int64` field to `XFTPEnv` record (between `store` and `storeLog`, line 93).
2. In `newXFTPServerEnv` (line 112-126): replace lines 117-118:
```
used <- countUsedStorage <$> readTVarIO (files store)
atomically $ writeTVar (usedStorage store) used
```
with:
```
usedStorage <- newTVarIO =<< countUsedStorage <$> readTVarIO (files store)
```
3. Add `usedStorage` to the `pure XFTPEnv {..}` construction.
- [ ] **Step 3: Update all `usedStorage` access sites in `Server.hs`**
1. Line 552: `us <- asks $ usedStorage . store``us <- asks usedStorage`.
2. Line 569: `us <- asks $ usedStorage . store``us <- asks usedStorage`.
3. Line 639: `usedStart <- readTVarIO $ usedStorage st``usedStart <- readTVarIO =<< asks usedStorage`.
4. Line 647: `usedEnd <- readTVarIO $ usedStorage st``usedEnd <- readTVarIO =<< asks usedStorage`.
5. Line 694: `FileStore {files, usedStorage} <- asks store` → split into `FileStore {files} <- asks store` and `usedStorage <- asks usedStorage`.
6. In `deleteOrBlockServerFile_` (line 620): after `void $ atomically $ storeAction st`, add usedStorage adjustment — `us <- asks usedStorage` then `atomically $ modifyTVar' us $ subtract (fromIntegral $ size fileInfo)` when file had a path (check `path` from `readTVarIO filePath` earlier in the function).
- [ ] **Step 4: Build and verify**
Run: `cabal build`
- [ ] **Step 5: Run existing tests**
Run: `cabal test --test-show-details=streaming --test-option=--match="/XFTP/"`
- [ ] **Step 6: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs
git add src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs
git commit -m "refactor(xftp): move usedStorage from FileStore to XFTPEnv"
```
---
## Task 2: Add `getUsedStorage`, `getFileCount`, `expiredFiles` functions
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Store.hs`
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- Modify: `src/Simplex/FileTransfer/Server.hs`
- [ ] **Step 1: Add three new functions to `Store.hs`**
1. Add to exports: `getUsedStorage`, `getFileCount`, `expiredFiles`.
2. Remove `expiredFilePath` from exports AND delete the function definition (dead code → `-Wunused-binds` error). Also remove `($>>=)` from import `Simplex.Messaging.Util (ifM, ($>>=))``Simplex.Messaging.Util (ifM)``$>>=` was only used by `expiredFilePath`.
3. Add import: `qualified Data.Map.Strict as M` (needed for `M.foldl'` in `getUsedStorage` and `M.toList` in `expiredFiles`).
4. Implement:
```haskell
getUsedStorage :: FileStore -> IO Int64
getUsedStorage FileStore {files} =
M.foldl' (\acc FileRec {fileInfo = FileInfo {size}} -> acc + fromIntegral size) 0 <$> readTVarIO files
getFileCount :: FileStore -> IO Int
getFileCount FileStore {files} = M.size <$> readTVarIO files
expiredFiles :: FileStore -> Int64 -> Int -> IO [(SenderId, Maybe FilePath, Word32)]
expiredFiles FileStore {files} old _limit = do
fs <- readTVarIO files
fmap catMaybes . forM (M.toList fs) $ \(sId, FileRec {fileInfo = FileInfo {size}, filePath, createdAt = RoundedSystemTime createdAt}) ->
if createdAt + fileTimePrecision < old
then do
path <- readTVarIO filePath
pure $ Just (sId, path, size)
else pure Nothing
```
5. Add imports: `Data.Maybe (catMaybes)`, `Data.Word (Word32)` (note: `qualified Data.Map.Strict as M` already added in item 3).
- [ ] **Step 2: Replace `countUsedStorage` in `Env.hs`**
1. Replace `countUsedStorage <$> readTVarIO (files store)` with `getUsedStorage store` in `newXFTPServerEnv`.
2. Remove `countUsedStorage` function definition and its export.
3. Remove `qualified Data.Map.Strict as M` import if no longer used.
- [ ] **Step 3: Update `restoreServerStats` in `Server.hs` to use `getFileCount`**
In `restoreServerStats` (line 694-696): replace `FileStore {files} <- asks store` and `_filesCount <- M.size <$> readTVarIO files` with `st <- asks store` and `_filesCount <- liftIO $ getFileCount st` (eliminates the `FileStore` pattern match — `files` binding no longer needed).
- [ ] **Step 4: Replace `expireServerFiles` iteration in `Server.hs`**
1. Replace the body of `expireServerFiles` (lines 636-660). Remove `files' <- readTVarIO (files st)` and the `forM_ (M.keys files')` loop.
2. New body: call `expiredFiles st old 10000` in a loop. For each `(sId, filePath_, fileSize)` in returned list: apply `itemDelay`, remove disk file if present, call `atomically $ deleteFile st sId`, adjust `usedStorage` TVar by `fileSize`, increment `filesExpired` stat. Loop until `expiredFiles` returns `[]`.
3. Remove `Data.Map.Strict` import from Server.hs if no longer needed (was used for `M.size` and `M.keys` — now replaced by `getFileCount` and `expiredFiles`).
- [ ] **Step 5: Build and verify**
Run: `cabal build`
- [ ] **Step 6: Run existing tests**
Run: `cabal test --test-show-details=streaming --test-option=--match="/XFTP/"`
- [ ] **Step 7: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs
git add src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs
git commit -m "refactor(xftp): add getUsedStorage, getFileCount, expiredFiles store functions"
```
---
## Task 3: Change `Store.hs` functions from STM to IO
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Store.hs`
- Modify: `src/Simplex/FileTransfer/Server.hs`
- Modify: `src/Simplex/FileTransfer/Server/StoreLog.hs`
- [ ] **Step 1: Change all Store.hs function signatures from STM to IO**
For each of: `addFile`, `setFilePath`, `addRecipient`, `getFile`, `deleteFile`, `blockFile`, `deleteRecipient`, `ackFile`:
1. Change return type from `STM (Either XFTPErrorType ...)` to `IO (Either XFTPErrorType ...)` (or `STM ()` to `IO ()` for `deleteRecipient`).
2. Wrap the function body in `atomically $ do ...`.
3. Keep `withFile` and `newFileRec` as internal STM helpers (called inside the `atomically` blocks).
- [ ] **Step 2: Update Server.hs call sites — remove `atomically` wrappers**
1. Line 563 (`receiveServerFile`): change `atomically $ writeTVar filePath (Just fPath)` → add `st <- asks store` then `void $ liftIO $ setFilePath st senderId fPath` (design call site #1`store` is not in scope in `receiveServerFile`'s `receive` helper, so bind via `asks`; `void` avoids `-Wunused-do-bind` warning on the `Either` result).
2. Line 453 (`verifyXFTPTransmission`): split `atomically $ verify =<< getFile st party fId` into: `liftIO (getFile st party fId)` (IO→M lift), then pattern match on result, use `readTVarIO (fileStatus fr)` instead of `readTVar`.
3. Lines 371, 377 (control port `CPDelete`/`CPBlock`): change `ExceptT $ atomically $ getFile fs SFRecipient fileId``ExceptT $ liftIO $ getFile fs SFRecipient fileId` (inside `unliftIO u $ do` block which runs in M monad — `liftIO` required to lift IO into M).
4. Line 508 (`addFile` in `createFile`): the `ExceptT $ addFile st sId file ts EntityActive``addFile` is now IO, `ExceptT` wraps IO directly. Remove any `atomically`.
5. Line 514 (`addRecipient`): same — `ExceptT . addRecipient st sId` works directly in IO.
6. Line 516 (`retryAdd`): change parameter type from `(XFTPFileId -> STM (Either XFTPErrorType a))` to `(XFTPFileId -> IO (Either XFTPErrorType a))`. Line 520: change `atomically (add fId)` to `liftIO (add fId)`.
7. Line 605 (`ackFileReception`): change `atomically $ deleteRecipient st rId fr` to `liftIO $ deleteRecipient st rId fr`.
8. Line 620 (`deleteOrBlockServerFile_`): change third parameter type from `(FileStore -> STM (Either XFTPErrorType ()))` to `(FileStore -> IO (Either XFTPErrorType ()))`. Line 626: change `void $ atomically $ storeAction st` to `void $ liftIO $ storeAction st`.
9. `expireServerFiles` `delete` helper: change `atomically $ deleteFile st sId` to `liftIO $ deleteFile st sId` (deleteFile is now IO; `liftIO` required because the helper runs in M monad, not IO).
- [ ] **Step 3: Update `StoreLog.hs` — remove `atomically` from replay**
In `readFileStore` (line 93), function `addToStore`:
1. Change `atomically (addToStore lr)` to `addToStore lr` — store functions are now IO.
2. The `addToStore` body calls `addFile`, `setFilePath`, `deleteFile`, `blockFile`, `ackFile` — all IO now, no `atomically` needed.
3. For `AddRecipients`: `runExceptT $ mapM_ (ExceptT . addRecipient st sId) rcps``addRecipient` returns `IO (Either ...)`, so `ExceptT . addRecipient st sId` works directly.
- [ ] **Step 4: Build and verify**
Run: `cabal build`
- [ ] **Step 5: Run existing tests**
Run: `cabal test --test-show-details=streaming --test-option=--match="/XFTP/"`
- [ ] **Step 6: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/StoreLog.hs
git add src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/StoreLog.hs
git commit -m "refactor(xftp): change file store operations from STM to IO"
```
---
## Task 4: Extract `FileStoreClass` typeclass, move STM impl to `Store/STM.hs`
**Files:**
- Rewrite: `src/Simplex/FileTransfer/Server/Store.hs`
- Create: `src/Simplex/FileTransfer/Server/Store/STM.hs`
- Modify: `src/Simplex/FileTransfer/Server/StoreLog.hs`
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- Modify: `src/Simplex/FileTransfer/Server.hs`
- Modify: `simplexmq.cabal`
- [ ] **Step 1: Create `Store/STM.hs` — move all implementation code**
1. Create directory `src/Simplex/FileTransfer/Server/Store/`.
2. Create `src/Simplex/FileTransfer/Server/Store/STM.hs`.
3. Move from `Store.hs`: `FileStore` data type (rename to `STMFileStore`), all function implementations, internal helpers (`withFile`, `newFileRec`), all STM-specific imports.
4. Rename all `FileStore` references to `STMFileStore` in the new file.
5. Module declaration: `module Simplex.FileTransfer.Server.Store.STM` exporting only `STMFileStore (..)` — do NOT export standalone functions (`addFile`, `setFilePath`, etc.) to avoid name collisions with the typeclass methods from `Store.hs`.
- [ ] **Step 2: Rewrite `Store.hs` as the typeclass module**
1. Add `{-# LANGUAGE TypeFamilies #-}` pragma to `Store.hs` (required for `type FileStoreConfig s` associated type).
2. Keep in `Store.hs`: `FileRec (..)`, `FileRecipient (..)`, `RoundedFileTime`, `fileTimePrecision` definitions and their `StrEncoding` instance.
3. Add `FileStoreClass` typeclass:
```haskell
class FileStoreClass s where
type FileStoreConfig s
-- Lifecycle
newFileStore :: FileStoreConfig s -> IO s
closeFileStore :: s -> IO ()
-- File operations
addFile :: s -> SenderId -> FileInfo -> RoundedFileTime -> ServerEntityStatus -> IO (Either XFTPErrorType ())
setFilePath :: s -> SenderId -> FilePath -> IO (Either XFTPErrorType ())
addRecipient :: s -> SenderId -> FileRecipient -> IO (Either XFTPErrorType ())
getFile :: s -> SFileParty p -> XFTPFileId -> IO (Either XFTPErrorType (FileRec, C.APublicAuthKey))
deleteFile :: s -> SenderId -> IO (Either XFTPErrorType ())
blockFile :: s -> SenderId -> BlockingInfo -> Bool -> IO (Either XFTPErrorType ())
deleteRecipient :: s -> RecipientId -> FileRec -> IO ()
ackFile :: s -> RecipientId -> IO (Either XFTPErrorType ())
-- Expiration
expiredFiles :: s -> Int64 -> Int -> IO [(SenderId, Maybe FilePath, Word32)]
-- Stats
getUsedStorage :: s -> IO Int64
getFileCount :: s -> IO Int
```
4. Do NOT re-export from `Store/STM.hs` — this would create a circular module dependency (Store.hs imports Store/STM.hs, Store/STM.hs imports Store.hs). Consumers must import `Store.STM` directly where they need `STMFileStore`.
5. Remove all STM-specific imports that are no longer needed.
- [ ] **Step 3: Add `FileStoreClass` instance in `Store/STM.hs`**
1. Import `FileStoreClass` from `Simplex.FileTransfer.Server.Store`.
2. Inline all implementations directly in the instance body (do NOT delegate to standalone functions — the standalone names collide with typeclass method names, causing ambiguous occurrences for importers):
```haskell
instance FileStoreClass STMFileStore where
type FileStoreConfig STMFileStore = ()
newFileStore () = do
files <- TM.emptyIO
recipients <- TM.emptyIO
pure STMFileStore {files, recipients}
closeFileStore _ = pure ()
addFile st sId fileInfo createdAt status = atomically $ ...
setFilePath st sId fPath = atomically $ ...
-- ... (each method's body is the existing function body, inlined)
```
3. Remove the standalone top-level function definitions — they are now instance methods. Keep only `withFile` and `newFileRec` as internal helpers used by the instance methods.
- [ ] **Step 4: Update importers**
1. `Env.hs`: add `import Simplex.FileTransfer.Server.Store.STM (STMFileStore (..))`. Change `FileStore``STMFileStore` in `XFTPEnv` type and `newXFTPServerEnv`. Change `store <- newFileStore` to `store <- newFileStore ()` (typeclass method now takes `FileStoreConfig STMFileStore` which is `()`). Keep `import Simplex.FileTransfer.Server.Store` for `FileRec`, `FileRecipient`, `FileStoreClass`, etc.
2. `Server.hs`: add `import Simplex.FileTransfer.Server.Store.STM`. Change `FileStore``STMFileStore` in any explicit type annotations. Import `FileStoreClass` from `Simplex.FileTransfer.Server.Store`.
3. `StoreLog.hs`: add `import Simplex.FileTransfer.Server.Store.STM` to access concrete `STMFileStore` type and store functions used during log replay. Change `FileStore``STMFileStore` in `readWriteFileStore` and `writeFileStore` parameter types.
- [ ] **Step 5: Update cabal file**
Add `Simplex.FileTransfer.Server.Store.STM` to `exposed-modules` in the `!flag(client_library)` section, alongside existing XFTP server modules.
- [ ] **Step 6: Build and verify**
Run: `cabal build`
- [ ] **Step 7: Run existing tests**
Run: `cabal test --test-show-details=streaming --test-option=--match="/XFTP/"`
- [ ] **Step 8: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Store/STM.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/StoreLog.hs
git add src/Simplex/FileTransfer/Server/Store.hs src/Simplex/FileTransfer/Server/Store/STM.hs src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/StoreLog.hs simplexmq.cabal
git commit -m "refactor(xftp): extract FileStoreClass typeclass, move STM impl to Store.STM"
```
---
## Task 5: Make `XFTPEnv` and `Server.hs` polymorphic over `FileStoreClass`
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- Modify: `src/Simplex/FileTransfer/Server.hs`
- Modify: `src/Simplex/FileTransfer/Server/Main.hs`
- Modify: `tests/XFTPClient.hs` (if it calls `runXFTPServerBlocking` directly)
- [ ] **Step 1: Make `XFTPEnv` polymorphic in `Env.hs`**
1. Add `XFTPStoreConfig` GADT: `data XFTPStoreConfig s where XSCMemory :: Maybe FilePath -> XFTPStoreConfig STMFileStore`.
2. Change `data XFTPEnv` to `data XFTPEnv s` — field `store :: FileStore` becomes `store :: s`.
3. Change `newXFTPServerEnv :: XFTPServerConfig -> IO XFTPEnv` to `newXFTPServerEnv :: FileStoreClass s => XFTPStoreConfig s -> XFTPServerConfig -> IO (XFTPEnv s)`.
4. Pattern match on `XSCMemory storeLogPath` in `newXFTPServerEnv` body. Create store via `newFileStore ()`, storeLog via `mapM (`readWriteFileStore` st) storeLogPath`.
- [ ] **Step 2: Make `Server.hs` polymorphic**
1. Change `type M a = ReaderT XFTPEnv IO a` to `type M s a = ReaderT (XFTPEnv s) IO a`.
2. Add `FileStoreClass s =>` constraint to all functions using `M s a`. Use `forall s.` in signatures of functions that have `where`-block bindings with `M s` type annotations — `ScopedTypeVariables` requires explicit `forall` to bring `s` into scope for inner type signatures (matching SMP's `smpServer :: forall s. MsgStoreClass s => ...` pattern). Full list: `xftpServer`, `processRequest`, `verifyXFTPTransmission`, `processXFTPRequest` and all its `where`-bound functions (`createFile`, `addRecipients`, `receiveServerFile`, `sendServerFile`, `deleteServerFile`, `ackFileReception`, `retryAdd`, `addFileRetry`, `addRecipientRetry`), `deleteServerFile_`, `blockServerFile`, `deleteOrBlockServerFile_`, `expireServerFiles`, `randomId`, `getFileId`, `withFileLog`, `incFileStat`, `saveServerStats`, `restoreServerStats`, `randomDelay` (inside `#ifdef slow_servers` CPP block). Also update `encodeXftp` (line 236) and `runCPClient` (line 339) which use explicit `ReaderT XFTPEnv IO` instead of the `M` alias — change to `ReaderT (XFTPEnv s) IO`.
3. Change `runXFTPServerBlocking` and `runXFTPServer` to take `XFTPStoreConfig s` parameter.
4. Add `closeFileStore store` call to the server shutdown path (in the `finally` block or `stopServer` equivalent — after saving stats, before logging "Server stopped"). This ensures Postgres connection pool and `dbStoreLog` are properly closed. For STM this is a no-op.
- [ ] **Step 3: Update `Main.hs` dispatch**
1. In `runServer`: construct `XSCMemory (enableStoreLog $> storeLogFilePath)`.
2. Add dispatch function that calls the updated `runXFTPServer` (which creates `started` internally):
```haskell
run :: FileStoreClass s => XFTPStoreConfig s -> IO ()
run storeCfg = runXFTPServer storeCfg serverConfig
```
3. Call `run` with the `XSCMemory` config.
- [ ] **Step 4: Update test helper if needed**
If `tests/XFTPClient.hs` calls `runXFTPServerBlocking` directly, update the call to pass an `XSCMemory` config. Check the `withXFTPServer` / `serverBracket` helper.
- [ ] **Step 5: Build and verify**
Run: `cabal build && cabal build test:simplexmq-test`
- [ ] **Step 6: Run existing tests**
Run: `cabal test --test-show-details=streaming --test-option=--match="/XFTP/"`
- [ ] **Step 7: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/Main.hs
git add src/Simplex/FileTransfer/Server/Env.hs src/Simplex/FileTransfer/Server.hs src/Simplex/FileTransfer/Server/Main.hs tests/XFTPClient.hs simplexmq.cabal
git commit -m "refactor(xftp): make XFTPEnv and server polymorphic over FileStoreClass"
```
---
## Task 6: Add Postgres config, migrations, and store skeleton
**Files:**
- Create: `src/Simplex/FileTransfer/Server/Store/Postgres/Config.hs`
- Create: `src/Simplex/FileTransfer/Server/Store/Postgres/Migrations.hs`
- Create: `src/Simplex/FileTransfer/Server/Store/Postgres.hs`
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- Modify: `simplexmq.cabal`
- [ ] **Step 1: Create `Store/Postgres/Config.hs`**
```haskell
module Simplex.FileTransfer.Server.Store.Postgres.Config
( PostgresFileStoreCfg (..),
defaultXFTPDBOpts,
)
where
import Simplex.Messaging.Agent.Store.Postgres.Options (DBOpts (..))
import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation)
data PostgresFileStoreCfg = PostgresFileStoreCfg
{ dbOpts :: DBOpts,
dbStoreLogPath :: Maybe FilePath,
confirmMigrations :: MigrationConfirmation
}
defaultXFTPDBOpts :: DBOpts
defaultXFTPDBOpts =
DBOpts
{ connstr = "postgresql://xftp@/xftp_server_store",
schema = "xftp_server",
poolSize = 10,
createSchema = False
}
```
- [ ] **Step 2: Create `Store/Postgres/Migrations.hs`**
Full migration module with `xftpServerMigrations :: [Migration]` and `m20260325_initial` containing CREATE TABLE SQL for `files` and `recipients` tables plus indexes. Follow SMP's `QueueStore/Postgres/Migrations.hs` pattern exactly: tuple list → `sortOn name . map migration`.
- [ ] **Step 3: Create `Store/Postgres.hs` with stub instance**
1. Define `PostgresFileStore` with `dbStore :: DBStore` and `dbStoreLog :: Maybe (StoreLog 'WriteMode)`.
2. `instance FileStoreClass PostgresFileStore` with `error "not implemented"` for all methods except `newFileStore` (calls `createDBStore` + opens `dbStoreLog`) and `closeFileStore` (closes both). `type FileStoreConfig PostgresFileStore = PostgresFileStoreCfg`.
3. Add `withDB`, `handleDuplicate`, `assertUpdated`, `withLog` helpers.
- [ ] **Step 4: Add `XSCDatabase` GADT constructor in `Env.hs` (CPP-guarded)**
```haskell
#if defined(dbServerPostgres)
import Simplex.FileTransfer.Server.Store.Postgres (PostgresFileStore)
import Simplex.FileTransfer.Server.Store.Postgres.Config (PostgresFileStoreCfg)
#endif
data XFTPStoreConfig s where
XSCMemory :: Maybe FilePath -> XFTPStoreConfig STMFileStore
#if defined(dbServerPostgres)
XSCDatabase :: PostgresFileStoreCfg -> XFTPStoreConfig PostgresFileStore
#endif
```
- [ ] **Step 5: Update cabal**
Add to existing `if flag(server_postgres)` block:
```
Simplex.FileTransfer.Server.Store.Postgres
Simplex.FileTransfer.Server.Store.Postgres.Config
Simplex.FileTransfer.Server.Store.Postgres.Migrations
```
- [ ] **Step 6: Build both ways**
Run: `cabal build && cabal build -fserver_postgres`
- [ ] **Step 7: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store/Postgres.hs src/Simplex/FileTransfer/Server/Store/Postgres/Config.hs src/Simplex/FileTransfer/Server/Env.hs
git add src/Simplex/FileTransfer/Server/Store/Postgres.hs src/Simplex/FileTransfer/Server/Store/Postgres/Config.hs src/Simplex/FileTransfer/Server/Store/Postgres/Migrations.hs src/Simplex/FileTransfer/Server/Env.hs simplexmq.cabal
git commit -m "feat(xftp): add PostgreSQL store skeleton with schema migration"
```
---
## Task 7: Implement `PostgresFileStore` operations
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Store/Postgres.hs`
- [ ] **Step 1: Implement `addFile`**
`INSERT INTO files (sender_id, file_size, file_digest, sender_key, file_path, created_at, status) VALUES (?,?,?,?,NULL,?,?)`. Catch unique violation with `handleDuplicate``DUPLICATE_`. Call `withLog "addFile"` after.
- [ ] **Step 2: Implement `getFile`**
For `SFSender`: `SELECT ... FROM files WHERE sender_id = ?`. Construct `FileRec` with `newTVarIO` per TVar field. `recipientIds = S.empty`.
For `SFRecipient`: `SELECT f.*, r.recipient_key FROM recipients r JOIN files f ON r.sender_id = f.sender_id WHERE r.recipient_id = ?`.
- [ ] **Step 3: Implement `setFilePath`**
`UPDATE files SET file_path = ? WHERE sender_id = ? AND file_path IS NULL`. Use `assertUpdated`. Call `withLog "setFilePath"`.
- [ ] **Step 4: Implement `addRecipient`**
`INSERT INTO recipients (recipient_id, sender_id, recipient_key) VALUES (?,?,?)`. `handleDuplicate``DUPLICATE_`. Call `withLog "addRecipient"`.
- [ ] **Step 5: Implement `deleteFile`, `blockFile`**
`deleteFile`: `DELETE FROM files WHERE sender_id = ?` (CASCADE). `withLog "deleteFile"`.
`blockFile`: `UPDATE files SET status = ? WHERE sender_id = ?`. `assertUpdated`. `withLog "blockFile"`.
- [ ] **Step 6: Implement `deleteRecipient`, `ackFile`**
`deleteRecipient`: `DELETE FROM recipients WHERE recipient_id = ?`. `withLog "deleteRecipient"`.
`ackFile`: same + return `Left AUTH` if 0 rows.
- [ ] **Step 7: Implement `expiredFiles`, `getUsedStorage`, `getFileCount`**
`expiredFiles`: `SELECT sender_id, file_path, file_size FROM files WHERE created_at + ? < ? LIMIT ?`.
`getUsedStorage`: `SELECT COALESCE(SUM(file_size), 0) FROM files`.
`getFileCount`: `SELECT COUNT(*) FROM files`.
- [ ] **Step 8: Add `ToField`/`FromField` instances**
For `RoundedFileTime` (Int64 wrapper), `ServerEntityStatus` (Text via StrEncoding), `C.APublicAuthKey` (Binary via `encodePubKey`/`decodePubKey`). Check SMP's `QueueStore/Postgres.hs` for existing instances to import.
- [ ] **Step 9: Wrap mutation operations in `uninterruptibleMask_`**
Operations that combine a DB write with a TVar update (e.g., `getFile` constructs `FileRec` with `newTVarIO`) must be wrapped in `E.uninterruptibleMask_` to prevent async exceptions from leaving inconsistent state. Follow SMP's `addQueue_`, `deleteStoreQueue` pattern.
- [ ] **Step 10: Build**
Run: `cabal build -fserver_postgres`
- [ ] **Step 11: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Store/Postgres.hs
git add src/Simplex/FileTransfer/Server/Store/Postgres.hs
git commit -m "feat(xftp): implement PostgresFileStore operations"
```
---
## Task 8: Add INI config, Main.hs dispatch, startup validation
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Main.hs`
- Modify: `src/Simplex/FileTransfer/Server/Env.hs`
- [ ] **Step 1: Update `iniFileContent` in `Main.hs`**
Add to `[STORE_LOG]` section: `store_files: memory`, commented-out `db_connection`, `db_schema`, `db_pool_size`, `db_store_log` keys. Follow SMP's `optDisabled'` pattern for commented defaults.
- [ ] **Step 2: Add `StartOptions` and `--confirm-migrations` flag**
```haskell
data StartOptions = StartOptions
{ confirmMigrations :: MigrationConfirmation
}
```
Add to `Start` command parser with default `MCConsole`. Thread through to `runServer`.
- [ ] **Step 3: Add store_files INI parsing and CPP-guarded Postgres dispatch**
In `runServer`: read `store_files` from INI (`fromRight "memory" $ lookupValue "STORE_LOG" "store_files" ini`). Add `"database"` branch (CPP-guarded) that constructs `PostgresFileStoreCfg` using `iniDBOptions ini defaultXFTPDBOpts` and `enableDbStoreLog'` pattern. Non-postgres build: `exitError`.
- [ ] **Step 4: Add `XSCDatabase` branch in `newXFTPServerEnv` (`Env.hs`)**
CPP-guarded pattern match on `XSCDatabase dbCfg`: `newFileStore dbCfg`, `storeLog = Nothing`.
- [ ] **Step 5: Add startup config validation**
Add `checkFileStoreMode` (CPP-guarded) before `run`: validate conflicting storeLog file + database mode, missing schema, etc. per design doc.
- [ ] **Step 6: Build both ways**
Run: `cabal build && cabal build -fserver_postgres`
- [ ] **Step 7: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Main.hs src/Simplex/FileTransfer/Server/Env.hs
git add src/Simplex/FileTransfer/Server/Main.hs src/Simplex/FileTransfer/Server/Env.hs
git commit -m "feat(xftp): add PostgreSQL INI config, store dispatch, startup validation"
```
---
## Task 9: Add database import/export CLI commands
**Files:**
- Modify: `src/Simplex/FileTransfer/Server/Main.hs`
- [ ] **Step 1: Add `Database` CLI command (CPP-guarded)**
Add `Database StoreCmd DBOpts` constructor to `CliCommand`. Add `database` subcommand parser with `import`/`export` subcommands + `dbOptsP defaultXFTPDBOpts`.
- [ ] **Step 2: Implement `importFileStoreToDatabase`**
1. `confirmOrExit` with database details.
2. Create temporary `STMFileStore`, replay StoreLog via `readWriteFileStore`.
3. Create `PostgresFileStore` with `createSchema = True`, `confirmMigrations = MCYesUp`.
4. Batch-insert files using PostgreSQL COPY protocol. Progress every 10k.
5. Batch-insert recipients using COPY protocol.
6. Verify counts: `SELECT COUNT(*)` — warn on mismatch.
7. Rename StoreLog to `.bak`.
8. Report counts.
- [ ] **Step 3: Implement `exportDatabaseToStoreLog`**
1. `confirmOrExit`. Fail if output file exists.
2. Create `PostgresFileStore` from config.
3. Open StoreLog for writing.
4. Fold over file records: write `AddFile` (with status), `AddRecipients`, `PutFile` per file.
5. Close StoreLog, report counts.
- [ ] **Step 4: Build**
Run: `cabal build -fserver_postgres`
- [ ] **Step 5: Format and commit**
```bash
fourmolu -i src/Simplex/FileTransfer/Server/Main.hs
git add src/Simplex/FileTransfer/Server/Main.hs
git commit -m "feat(xftp): add database import/export CLI commands"
```
---
## Task 10: Add Postgres tests
**Files:**
- Modify: `tests/XFTPClient.hs`
- Modify: `tests/Test.hs`
- Create: `tests/CoreTests/XFTPStoreTests.hs`
- [ ] **Step 1: Add test fixtures in `tests/XFTPClient.hs`**
```haskell
testXFTPStoreDBOpts :: DBOpts
testXFTPStoreDBOpts =
DBOpts
{ connstr = "postgresql://test_xftp_server_user@/test_xftp_server_db",
schema = "xftp_server_test",
poolSize = 10,
createSchema = True
}
```
Add `testXFTPDBConnectInfo :: ConnectInfo` matching the connection string.
- [ ] **Step 2: Add Postgres server test group in `tests/Test.hs`**
CPP-guarded block that runs existing `xftpServerTests` with Postgres store config, wrapped in `postgressBracket testXFTPDBConnectInfo`. Parameterize `withXFTPServer` to accept store config if needed.
- [ ] **Step 3: Create `tests/CoreTests/XFTPStoreTests.hs` — unit tests**
Test `PostgresFileStore` operations directly:
- `addFile` + `getFile SFSender` round-trip.
- `addFile` duplicate → `DUPLICATE_`.
- `getFile` nonexistent → `AUTH`.
- `setFilePath` + verify `WHERE file_path IS NULL` guard.
- `addRecipient` + `getFile SFRecipient` round-trip.
- `deleteFile` cascades recipients.
- `blockFile` + verify status.
- `expiredFiles` batch semantics.
- `getUsedStorage`, `getFileCount` correctness.
- [ ] **Step 4: Add migration round-trip test**
Create `STMFileStore` with test data (files + recipients + blocked status) → export to StoreLog → import to Postgres → export back → compare StoreLog files byte-for-byte.
- [ ] **Step 5: Build and run tests**
```bash
cabal build -fserver_postgres test:simplexmq-test
cabal test --test-show-details=streaming --test-option=--match="/XFTP/" -fserver_postgres
```
- [ ] **Step 6: Format and commit**
```bash
fourmolu -i tests/CoreTests/XFTPStoreTests.hs tests/XFTPClient.hs
git add tests/CoreTests/XFTPStoreTests.hs tests/XFTPClient.hs tests/Test.hs
git commit -m "test(xftp): add PostgreSQL backend tests"
```
+126
View File
@@ -0,0 +1,126 @@
# BBS+ Bindings for simplexmq
Haskell FFI bindings to libbbs for BBS+ signatures. General-purpose - the module knows nothing about specific applications.
## How BBS+ works
BBS+ signs a fixed list of N messages. Each message is an arbitrary byte array. The signer signs all N messages at once with one signature.
The holder of the signature can then generate a proof that selectively discloses some messages and hides others. The verifier learns the disclosed messages and confirms they were signed by the signer, but learns nothing about the hidden messages. Different proofs from the same signature are unlinkable.
Key constraint: the total number of messages N is fixed at signing time. The verifier must know N. A proof generated from a 3-message signature cannot be verified as a 2-message proof.
## Types
```haskell
newtype BBSSecretKey = BBSSecretKey ByteString -- 32 bytes
newtype BBSPublicKey = BBSPublicKey ByteString -- 96 bytes (BLS12-381 G2 point)
newtype BBSSignature = BBSSignature ByteString -- 80 bytes
newtype BBSProof = BBSProof ByteString -- 272 + 32 * numUndisclosed bytes
newtype BBSHeader = BBSHeader ByteString -- always-disclosed context (e.g. protocol identifier)
newtype BBSPresHeader = BBSPresHeader ByteString -- random nonce for proof unlinkability
```
All newtypes get StrEncoding (base64url), ToJSON/FromJSON (via strToJSON/strParseJSON), Eq, Show.
## Functions
```haskell
bbsKeyGen :: IO (Either String BBSKeyPair) -- BBSKeyPair = (BBSPublicKey, BBSSecretKey)
-- pk is derived from sk internally, so it is not a parameter
bbsSign
:: BBSSecretKey
-> BBSHeader -- always-disclosed context
-> [ByteString] -- all N messages
-> IO (Either String BBSSignature)
-- C order: pk, signature, header, presentation_header, disclosed_indexes, messages
bbsProofGen
:: BBSPublicKey
-> BBSSignature
-> BBSHeader -- must match what was signed
-> BBSPresHeader -- random nonce bound into the proof
-> [Int] -- disclosed indexes (0-based)
-> [ByteString] -- all N messages (needed internally, hidden ones not revealed in proof)
-> IO (Either String BBSProof)
-- C order: pk, proof, header, presentation_header, disclosed_indexes, n, messages
bbsProofVerify
:: BBSPublicKey
-> BBSProof
-> BBSHeader -- must match what was signed
-> BBSPresHeader -- must match what was used in bbsProofGen
-> [Int] -- disclosed indexes
-> Int -- total message count N
-> [ByteString] -- disclosed messages only
-> IO Bool
```
## How applications use it
An application defines:
- A message layout: which index means what
- Which indexes are disclosed vs hidden
- How to encode application values as ByteString messages
### Badge example (in simplex-chat, not in this module)
Message layout (always 3 messages):
- Index 0: master secret (32 random bytes) - HIDDEN
- Index 1: expiry (UTF-8 encoded timestamp string) - DISCLOSED
- Index 2: badge type (UTF-8 encoded, e.g. "supporter") - DISCLOSED
Signing (v2, on the server):
```
bbsSign sk header [ms, encodeUtf8 "2026-07-31", encodeUtf8 "supporter"]
```
Proof generation (v2, on the client):
```
bbsProofGen pk sig header presHeader [1, 2] [ms, encodeUtf8 "2026-07-31", encodeUtf8 "supporter"]
```
Proof verification (v1, on the recipient):
```
bbsProofVerify pk proof header presHeader 3 [1, 2] [encodeUtf8 "2026-07-31", encodeUtf8 "supporter"]
```
The recipient only sees the proof, presentationHeader, expiry string, and badge type string. They verify these were signed by the server (pk is hardcoded). They never see the master secret.
Expiry is always present as a string. Monthly badges use a date like `"2026-07-31"`, lifetime badges use `"lifetime"`. BBS+ doesn't interpret the bytes - expiry semantics are the application's responsibility. This keeps the message count fixed at 3 for all badge types.
## libbbs C API mapping
```c
int bbs_keygen_full(ciphersuite, sk, pk)
int bbs_sign(ciphersuite, sk, pk, signature, header, header_len, n, messages, message_lens)
int bbs_proof_gen(ciphersuite, pk, signature, proof, header, header_len, presentation_header, presentation_header_len, disclosed_indexes, disclosed_indexes_len, n, messages, message_lens)
int bbs_proof_verify(ciphersuite, pk, proof, proof_len, header, header_len, presentation_header, presentation_header_len, disclosed_indexes, disclosed_indexes_len, n, messages, message_lens)
```
We use `bbs_sha256_ciphersuite`. The header parameter is exposed in all Haskell functions - the application decides what to put there. Tests use `"SimpleX"` as header.
The `presentation_header` parameter is what we call `presentationHeader`.
In `bbs_proof_verify`, the `n` parameter is the total number of messages (not the number of disclosed messages). The `messages` array contains only the disclosed messages, and `disclosed_indexes` maps each to its position in the original message list.
## Build
Submodules in cbits/:
- `cbits/libbbs` - https://github.com/Fraunhofer-AISEC/libbbs
- `cbits/blst` - https://github.com/supranational/blst (libbbs dependency)
C sources in cabal: `cbits/blst/src/server.c`, `cbits/blst/build/assembly.S`, libbbs source files.
Include dirs: `cbits/blst/bindings/`, `cbits/blst/src/`, `cbits/libbbs/include/`, `cbits/libbbs/src/`.
C flags: `-D__BLST_PORTABLE__` for cross-CPU-generation compatibility.
## Tests
- Keygen produces keys of correct size
- Sign + proofGen + proofVerify roundtrip succeeds
- Tampered proof fails verification
- Tampered disclosed message fails verification
- Wrong public key fails verification
- Two proofs from same credential with different nonces both verify
- Proof size matches expected (272 + 32 * numUndisclosed)
@@ -0,0 +1,57 @@
## Root cause: orphaned `Sub` entries in the service client's `subscriptions` map
**The leak is service-specific and was introduced by PR #1667 "messaging services" (`f0b7a4be`).** A long-lived messaging-service connection accumulates per-queue `Sub` records in its `Client.subscriptions` map that are **never removed** when the associated queues are deleted or unassociated — only the counter is decremented. Over normal queue churn the map grows monotonically for the entire lifetime of the service connection.
### The proof — an asymmetry between two handlers in `serverThread`
Both individual queue subscriptions and service subscriptions store a `Sub` per queue in `Client.subscriptions` (= `clientSubs` for the SMP subscriber thread, wired at `Server.hs:189`). When a queue ends/is deleted, the two paths diverge:
**Individual subscriber — entry IS removed** (`Server.hs:332`, `346`):
```haskell
CSAEndSub qId -> atomically (endSub c qId) >>= a unsub_ -- :332
...
endSub c qId = TM.lookupDelete qId (clientSubs c) >>= (removeWhenNoSubs c $>) -- :346
```
**Service subscriber — entry is NOT removed** (`Server.hs:336-340`):
```haskell
CSAEndServiceSub qId -> atomically $ do
modifyTVar' (clientServiceSubs c) decrease -- decrements serviceSubsCount
modifyTVar' totalServiceSubs decrease -- decrements global count
where decrease = subtractServiceSubs (1, queueIdHash qId)
-- never touches (clientSubs c) — the Sub for qId stays forever
```
### Where the orphaned entries are added (both new in this PR)
- `Server.hs:1860-1862` — on service subscribe (`SSUB`), one `Sub` inserted per queue that has a pending message.
- `Server.hs:2039-2043` (`newServiceDeliverySub`) — on **every** `SEND` to a service-associated queue with no existing sub, a `Sub` is inserted into the service client's `subscriptions`. After delivery the thread state resets to `NoSub` (`:2069`) but the map entry remains as a "already delivering" marker (`:1856-1859`).
### Why they leak
The only places the service client's `subscriptions` map is cleared are:
- `clientDisconnected``swapTVar subscriptions M.empty` (`Server.hs:1097`) — only on disconnect.
- `CSADecreaseSubs``swapTVar (clientSubs c) M.empty` (`Server.hs:343`) — only on full service takeover by another connection.
- `delQueueAndMsgs``TM.lookupDelete entId $ subscriptions clnt` (`Server.hs:2164`) — but `clnt` here is **the recipient deleting its own queue, not the service client**. The service's entry for that queue is reached only via the `CSDeleted → endServiceSub → CSAEndServiceSub` path (`Server.hs:306, 313, 336`), which decrements the counter but leaves the map entry.
**Concrete scenario (fully traced):** Service `S` subscribes (`SSUB`) and stays connected for days. Recipient `R` owns service-associated queue `Q`. A `SEND` to `Q` inserts a `Sub` into `S.subscriptions[Q]` (`:2042`). `R` later deletes `Q``delQueueAndMsgs` runs on `R`'s connection, removes `Q` from `R.subscriptions`, decrements counters, enqueues `CSDeleted Q (Just S)` (`:2167`) → `serverThread` runs `CSAEndServiceSub Q` for `S` (`:336`), decrementing `S.serviceSubsCount` but **leaving `S.subscriptions[Q]` in place**. Net: one orphaned `Sub` (record + 2 TVars) per service-associated queue ever deleted/unassociated, never reclaimed until `S` disconnects. The logical counter `serviceSubsCount` correctly drops, so the map size diverges from the counter — making the leak invisible to the existing service-sub metric.
### Verdict
This is a deterministic, static-provable memory leak — no production logging needed to confirm the existence; the asymmetry between `CSAEndSub` (removes) and `CSAEndServiceSub` (doesn't) is the smoking gun. It is specific to messaging-service certificate clients, which is exactly the population added by the services/certificate PR.
### Secondary findings (lower impact, same PR area, not the primary cause)
- **`forkClient` register-after-fork race** (`Server.hs:1356-1359`): if the forked action's `finally` delete (`:1358`) runs before the parent's `IM.insert` (`:1359`), a `Weak ThreadId` of a dead thread is left in `endThreads` until disconnect. Pre-existing, tiny per-entry, but exercised far more by the PR's higher END/DELD volume.
- **Wrong-client counter decrement** (`Server.hs:2166`): `delQueueAndMsgs` decrements `serviceSubsCount` of the *deleting* client, not the service; harmless for non-service deleters (floored at 0) but corrupts accounting if a service deletes its own queue.
---
### Recommended fix (mirror `endSub` in the service path)
Make `CSAEndServiceSub` also delete the per-queue `Sub` and cancel its delivery thread, exactly as `CSAEndSub`/`endSub` do for individual subscribers. Roughly:
```haskell
CSAEndServiceSub qId -> do
s_ <- atomically $ do
modifyTVar' (clientServiceSubs c) decrease
modifyTVar' totalServiceSubs decrease
TM.lookupDelete qId (clientSubs c) <* removeWhenNoSubs c
forM_ unsub_ $ \unsub -> mapM_ unsub s_
where decrease = subtractServiceSubs (1, queueIdHash qId)
```
@@ -0,0 +1,135 @@
# Service RPC implementation plan
RFC: [../rfcs/2026-07-11-service-rpc.md](../rfcs/2026-07-11-service-rpc.md)
Depends on: [2026-07-12-address-dr-implementation.md](2026-07-12-address-dr-implementation.md). RPC establishes the double ratchet from the address exactly as address-DR does; this is the RPC layer on top of it.
**Status: implemented and tested in this repo.** Service-side idempotency (single execution by request hash) is deferred. The `simplex-chat` integration is a separate repo.
**Scope.** One request, one response — no continuation, no streaming.
One DR-advertising contact address serves both flows: the owner branches per incoming request on the decrypted inner message — `AgentConnInfoReply` opens a connection (`REQ`), `AgentServiceRequest` answers an RPC (`SREQ`). A request gets exactly one reply, a response or a rejection; both are the single confirming message on the requester's reply queue Q_A, after which the ephemeral reply connection is torn down. Response and rejection are the same operation parameterized by the inner message (`AgentServiceResponse` payload vs `AgentRejection` reason) and outcome (the call returns the payload vs throws an agent error).
## RPC messages
No new outer envelope: the request reuses `AgentContactRequest` (tag `'A'`); the reply reuses `AgentConfirmation` (the only message on Q_A).
Inner `AgentMessage` (ratchet-encrypted, parsed by `parseMessage`), siblings of `AgentConnInfoReply`:
```haskell
| AgentServiceRequest (NonEmpty SMPQueueInfo) MsgBody -- 'A': reply queue Q_A + opaque payload
| AgentServiceResponse MsgBody -- 'P': response payload (single, terminal)
| AgentRejection ByteString -- 'J': refusal reason (single, terminal)
```
`AgentServiceRequest` carries Q_A (as `AgentConnInfoReply` does); its constructor is the only thing that distinguishes `REQ` from `SREQ`. Delivery `msgType`: `AM_SRV_RESP` routes to `sendConfirmation` (the reply is the confirming first message on Q_A). `AM_SRV_REQ` is never stored — the request is sent synchronously inside `joinConnSrv'` via `sendInvitation`, so its arms in the delivery worker are unreachable and assert (`logError`).
## Ratchet establishment — reuse of the address-DR flow
`joinConnSrv'` takes `mkInner :: SMPQueueInfo -> AgentMessage`; `joinConnSrv` is the one-line wrapper passing `AgentConnInfoReply`. `sendServiceRequest'` passes `AgentServiceRequest`.
**Request (client).** `serviceRequest_` fails fast with `A_SERVICE ASENotDRAddress` if the address carries no ratchet keys, then creates the client connection via `newConnToJoin` with `serviceRequestExpiresAt = Just (now + reqTimeout)` (the persisted per-request deadline), registers a one-shot `TMVar` in `serviceRequests`, sends the request, and blocks on the `TMVar` up to `reqTimeout`. The connection is `RcvConnection` (Q_A) with the send ratchet.
**Request (service).** `smpContactRequest` decrypts `encConnInfo` and branches on the inner message; both branches call the same `storeInvitation``conn_invitations`, differing only in the kind column and event:
- `AgentConnInfoReply``REQ` (`service_request = 0`).
- `AgentServiceRequest _ payload``SREQ invId payload` (`service_request = 1`).
Before storing, it **deduplicates** by the sender's ratchet-key hash (`checkRatchetKeyHashExists`/`addProcessedRatchetKeyHash`, the mechanism `newRatchetKey` uses): a redelivered/retried request reuses the same Q_A and the same `e2eSndParams`, so the hash matches and the duplicate is dropped — one invitation and one `REQ`/`SREQ` per request. Receive-time establishment on unauthenticated input — the address-DR abuse bound applies.
**The reply (service).** `prepareReply` fetches the invitation, enforces the kind (`CMD PROHIBITED` on the wrong one), and rejects a stale request (`A_SERVICE ASETimeout` + delete) older than `serviceResponseTimeout`; then `newConnToAccept` + `startJoinInvitationDR` build the one-directional `SndQueue` to Q_A (no reply queue back), and `storeConfirmation` queues the inner message. `sendReplySync` secures Q_A, submits the message, and deletes the connection with wait-for-delivery — **deleting the connection on failure too** (`catchAllErrors`), so a failed secure/submit does not orphan it. `sendServiceReplyAsync` defers secure+deliver+delete to the `ICReplyDel` command (retried, survives a down server). `sendServiceReply`/`Async` and `replyRequest_` return the reply `ConnId` so the caller can correlate the `SENT` event on that throwaway connection.
**The response (client).** The single `AgentConfirmation` on Q_A reaches `processConnInfo` (the `RcvConnection … New` branch). Dispatch is gated on `serviceRequestExpiresAt` and the kinds are mutually exclusive:
- `AgentConnInfoReply` **only when `isNothing serviceRequestExpiresAt`** (a contact connection) → `processConf`.
- `AgentServiceResponse` only when `isJust` → the request `TMVar` gets `Right payload`.
- `AgentRejection` when `isJust``Left (A_SERVICE (ASERejected reason))`; when `isNothing` → contact `RJCT`.
- anything else → `prohibited`.
The `isNothing` guard on `AgentConnInfoReply` is a security boundary: without it a malicious service could send `AgentConnInfoReply` on an RPC reply queue and drive it into the contact-`CONF` path. `dispatchServiceReply` puts the result into the `serviceRequests` `TMVar`; a reply with no pending request (e.g. post-restart) is `ERR (A_SERVICE ASENoPendingRequest)`.
## Rejection
A rejection is `AgentRejection reason` — the same single confirming message on Q_A as a response.
- **Kind guard.** `rejectContact` only on a contact invitation, `rejectServiceRequest`/`sendServiceReply` only on a request; wrong kind is `CMD PROHIBITED`. `rejectRequest_` enforces the kind **even on a `Nothing` (silent-drop) reject** — it fetches the invitation and checks before deleting, so `rejectContact … Nothing` cannot delete a service request (or vice versa).
- `reject*` take `Maybe ByteString`: `Nothing` → delete the invitation, send nothing (the requester times out); `Just reason` → the reply path with `AgentRejection`.
- Requester side: `AgentRejection` on a contact reply queue → `RJCT`; on an RPC reply queue → a thrown `A_SERVICE (ASERejected reason)`.
## Reply connections and cleanup
No reply-queue table and no new connection type.
- **Requester reply queue** (`RcvConnection` on Q_A): `connections.service_request_expires_at` is non-null only here; it is the persisted request deadline, used both to gate CONF dispatch and to reap the connection. In-memory routing is `serviceRequests :: TMap ConnId (TMVar (Either AgentErrorType MsgBody))`.
- **Timeout race.** The async `JOIN` worker holds `withConnLock c connId` around `joinConnSrv'`, and `serviceRequest_`'s cleanup holds the same lock around `TM.delete` + `deleteConnectionAsync'`. This serializes the send with the timeout teardown, so a timing-out call cannot delete the connection mid-send; after cleanup the worker's re-check of `serviceRequests` finds nothing and skips.
- **Service reply connection** (`SndConnection` to Q_A): ephemeral — created, sends the one reply, deleted with wait-for-delivery in the same operation.
- **Cleanup** (`cleanupManager`, `deleteExpiredServiceReqs`): `deleteExpiredServiceRequests` reaps unanswered `conn_invitations` (service side) older than `serviceResponseTimeout`; `getExpiredServiceConns` (`service_request_expires_at < now`) → `deleteConnectionsAsync'` reaps orphaned requester reply queues.
## Database schema
`M20260712_address_dr_rpc` (SQLite + PostgreSQL) creates `address_ratchet_keys` (address-DR) and adds:
```sql
ALTER TABLE conn_invitations ADD COLUMN service_request INTEGER NOT NULL DEFAULT 0; -- service side: 1 = RPC request
ALTER TABLE connections ADD COLUMN service_request_expires_at TEXT; -- client side: request deadline; gating + cleanup (nullable)
```
The down migration drops the columns then the table/index. Schema dump tests pass (up, down, STRICT).
## Agent API — `Simplex.Messaging.Agent`
```haskell
-- service: send the one response, return the reply ConnId, then tear the reply connection down.
sendServiceReply :: AgentClient -> NetworkRequestMode -> UserId -> InvitationId -> MsgBody -> AE ConnId
sendServiceReplyAsync :: AgentClient -> ACorrId -> UserId -> InvitationId -> MsgBody -> AE ConnId
-- refuse a request (Just reason = AgentRejection; Nothing = silent drop). PROHIBITED on wrong kind.
rejectServiceRequest :: AgentClient -> NetworkRequestMode -> UserId -> InvitationId -> Maybe ByteString -> AE ()
rejectServiceRequestAsync :: AgentClient -> ACorrId -> UserId -> InvitationId -> Maybe ByteString -> AE ()
rejectContact :: AgentClient -> NetworkRequestMode -> UserId -> ConfirmationId -> Maybe ByteString -> AE ()
rejectContactAsync :: AgentClient -> ACorrId -> UserId -> ConfirmationId -> Maybe ByteString -> AE ()
-- client: establish the ratchet from the address, send the request, block on the reply TMVar up to the timeout
-- (Nothing = serviceRequestTimeout; Just t overrides per request), returning the payload. Sync fails fast if the
-- server is down; async enqueues a retried JOIN command that survives an outage.
sendServiceRequest :: AgentClient -> NetworkRequestMode -> UserId -> ConnectionRequestUri 'CMContact -> Maybe NominalDiffTime -> MsgBody -> AE MsgBody
sendServiceRequestAsync :: AgentClient -> UserId -> ConnectionRequestUri 'CMContact -> Maybe NominalDiffTime -> MsgBody -> AE MsgBody
```
Both client calls share `serviceRequest_`; the async `JOIN` worker branches on the `JRServiceReq` command to send `AgentServiceRequest`. The call blocks on the `TMVar` and returns synchronously — no events, no correlation for the app.
Events (`AEvent`, entity is the address connection):
```haskell
SREQ :: InvitationId -> MsgBody -> AEvent AEConn -- payload = the request; mirrors REQ.
RJCT :: ConnInfo -> AEvent AEConn -- contact-request rejection reason.
```
Errors (`SMPAgentError`):
```haskell
| A_SERVICE {serviceError :: AgentServiceError}
data AgentServiceError
= ASERejected {rejectReason :: Text} -- service refused (Text: JSON-serializable, UTF-8-decoded from the reason bytes)
| ASETimeout -- no reply within the timeout
| ASENoPendingRequest -- a reply arrived with no pending request (e.g. post-restart)
| ASENotDRAddress -- the target address advertises no ratchet keys (fail fast, no send)
```
Config (`AgentConfig`): `serviceRequestTimeout` (30 s, client wait, overridable per request) and `serviceResponseTimeout` (180 s, service reply window and cleanup TTL; must exceed `serviceRequestTimeout`).
## Idempotency (deferred)
Not built. When built, the service will key a request by hash and cache the one response for a retention period, answering a repeat from storage without reaching the bot — single execution over at-least-once delivery, with its own tables.
## Tests
In `FunctionalAPITests` (plus the encoding roundtrip in `ConnectionRequestTests`), passing with `-O0`:
- Request → one response, sync (`sendServiceReply`) and async (`sendServiceReplyAsync`).
- Request → rejection (`rejectServiceRequest (Just reason)` → thrown `A_SERVICE (ASERejected …)`).
- Resilience: `server down → send → up → receive → down → reply → up → receive response`.
- No regression: the contact rejection and DR-join suites still pass.
The `simplex-chat` end-to-end tests (happy path, drop-when-off, non-DR fail-fast) live in that repo.
@@ -0,0 +1,279 @@
# Establishing the double ratchet from address data - implementation plan
RFC: [../rfcs/2026-07-12-address-pqdr-keys.md](../rfcs/2026-07-12-address-pqdr-keys.md)
All references are to the current tree. Names of new constructors, fields, tables and functions are provisional.
Goal: a contact address advertises the owner's X3DH parameters in link data; a requester establishes the double ratchet in its first message, so that message and the profile in it are under the ratchet with post-quantum protection. The change reuses the invitation/confirmation machinery, with the requester in the joiner role and the owner in the initiator role - opposite to today's contact flow, but every message and code path below is reused.
Version: `addressDRVersion = VersionSMPA 8`, a plain agent-layer bump; `currentSMPAgentVersion` goes 7 → 8 (Agent/Protocol.hs:317-324). It gates the `AgentConfirmation.ratchetKeyId` field and the DR-from-address behavior. The receive-at-address path relies on ratchet-on-confirmation, already present since `ratchetOnConfSMPAgentVersion = 7` (Agent/Protocol.hs:317), so there is no cross-layer version dependency; the SMP and e2e-encryption versions are unchanged.
Scope of this change: the **synchronous** DR handshake in join, gated on the address advertising `ratchetKeys`. `joinConnection`/`joinConn`/`joinConnSrv` gain an optional `Maybe AddressRatchetKeys` (the advertised `RcvE2ERatchetParamsUri` + `ratchetKeyId`), passed in from the link data the caller fetched at plan time (`LGET`); present → DR path (R2'/R3'), absent → the classic `AgentInvitation`. Chat wires that argument later (a chat change); the agent supports it now and tests pass it directly. Making the send **async** (worker retry, a "connecting" UX, the `CreatedConnLink` LGET-gate) is **deferred** - kept below under "Deferred" as future work, not part of this change.
### Implementation status (as built; `lib:simplexmq` compiles)
**Done** (compiles): version bump; `RatchetKeyId`/`AddressRatchetKeys` types + `Encoding`, `UserContactData.ratchetKeys` (appended, backward-compatible); `AgentConfirmation.ratchetKeyId` (version-gated encode/decode); `ContactRequest`/`DRRequest` sum with tagged `Encoding` + `cr_invitation` `ToField`/`FromField` (legacy-URI fallback); `address_ratchet_keys` table + `createAddressRatchetKeys`/`getAddressRatchetKeys` (SQLite + Postgres migrations `M20260712_address_dr`); join threading (`Maybe AddressRatchetKeys`); requester R2'/R3' (`joinAddressDR` + `sendConfirmationToAddress`); owner O1' dispatch, O2' `smpAddressConfirmation`, O3' (`acceptContact'` continue-ratchet branch), all three `connReq` readers (`acceptContact'`, `acceptContactAsync'``CMD PROHIBITED` for DR, `newConnToAccept` → shell from `drAgentVersion`/`drPQSupport`); requester R5' (`smpConfirmation` `RcvConnection … Nothing` branch, guarded on a ratchet existing); address-creation bundle generation (`mkAddressRatchetKeys`) wired into `createConnectionForLink'` (`IKUsePQ`-for-`SCMContact` prohibition lifted there).
**Deltas from the plan discovered while building:**
- **R5' emits `CONF` and reuses the allow step** (not auto-complete). The DR requester is a `RcvConnection` receiving the owner's reply - the same position as the classic contact requester, which goes `CONF``allowConnection'``connectReplyQueues` (msg 3). R5' mirrors that (differing only in that the ratchet already exists, so it `getRatchet` + `rcDecrypt` instead of building it), so the app supplies `ownConnInfo` for msg 3 at allow, exactly as today. No new storage.
- **`DRRequest` carries `drAgentVersion` + `drPQSupport`** (Part 3): the sync accept creates the connection shell via `newConnToAccept``newConnToJoin` before O3', and there is no URI to derive the version/PQ from.
- `cr_invitation` serialization is downgrade-safe: `CRInvitation` keeps the legacy URI (`strEncode`, byte-identical to before), so an older agent still reads classic invitations; `CRConfirmation` is JSON (`DRRequest` has manual `ToJSON`/`FromJSON`), told apart on read by the leading `{` (a URI never starts with it). JSON keeps `DRRequest` extensible. `SMPQueueInfo` gained a base64 `StrEncoding` + JSON (it only had `Encoding`) so it can sit in the JSON.
- **DR is opt-in per address**: `createConnectionForLink'`/`createConnectionForLink` gain a `Maybe InitialKeys` DR parameter (separate from the existing connection-PQ `InitialKeys`) - `Nothing` = no DR (old behavior, existing callers), `Just ik` = advertise the bundle with `ik`. The `IKUsePQ`-for-`SCMContact` prohibition stays on the connection-PQ parameter and is lifted only for the DR bundle.
**Test-matrix consequence of the version bump:** `currentSMPAgentVersion` 7 → 8 moves the version-matrix "prev" (`current 1`) from v6 to v7. v7 ≥ `ratchetOnConfSMPAgentVersion (7)`, so a joiner/acceptor at "prev" now secures the send queue on confirmation - the `sqSecured` expectation for the prev variants in `testMatrix2`/`testMatrix2Stress`/`testBasicMatrix2` flips `False → True`. (Standard version-bump maintenance; the pre-`ratchetOnConf` unsecured path is now two versions back and no longer exercised by these matrices.)
**Not yet done:** rotation (`rotateRatchetKeys`, Part 4), cleanup step (Part 4), the app-driven `LSET` upgrade API (Part 5), wiring the DR parameter into the non-prepared-link `newRcvConnSrv` path, DR-specific tests (Part 6), regenerating `agent_schema.sql` if a schema-consistency test requires it, and chat wiring (deferred by design).
## Part 1 - the current contact-address handshake, step by step
Requester Alice connects to owner Bob's contact address. Q_A is Alice's receive queue (Bob to Alice), Q_B is Bob's receive queue (Alice to Bob).
Requester side, in `joinConnSrv … CRContactUri` (Agent.hs:1398-1428):
- R1. `compatibleContactUri` (Agent.hs:1370) - version check, yields the address queue `SMPQueueInfo`.
- R2. `mkJoinInvitation` (Agent.hs:1411): creates or reuses the receive queue Q_A; `getRatchetX3dhKeys` or `generateRcvE2EParams` produces Alice's Rcv X3DH parameters, stored by `createRatchetX3dhKeys` (Agent.hs:1424); builds `cReq = CRInvitationUri crData aliceRcvParams` (Agent.hs:1426).
- R3. `sendInvitation` (Agent.hs:1408; Agent/Client.hs:1924-1934): sends `AgentInvitation {connReq = cReq, connInfo = aliceProfile}` to the address queue, per-queue encrypted with a fresh ephemeral key by `agentCbEncryptOnce` (Agent/Client.hs:1929-1934), unauthenticated. **`connInfo` (Alice's profile) is under the per-queue X25519 layer only - the gap this plan closes.**
Owner side, receiving on the contact address:
- O1. `processClientMsg` dispatch (Agent.hs:3185): state `(Nothing, Just e2ePubKey)`, `(PHEmpty, AgentInvitation {connReq, connInfo})` -> `smpInvitation` (Agent.hs:3186).
- O2. `smpInvitation` (Agent.hs:3610): stores an `Invitation`, emits `REQ` with Alice's `connInfo`.
- O3. `acceptContact'` (Agent.hs:1477): `getInvitation`, then `joinConn` with Alice's `connReq` (Agent.hs:1480).
- O4. `joinConnSrv … CRInvitationUri` (Agent.hs:1383) -> `startJoinInvitation` (Agent.hs:1395).
- O5. `startJoinInvitation` (Agent.hs:1310-1350): creates Bob's send queue to Q_A (`newSndQueue`, Agent.hs:1335); `createRatchet_` (Agent.hs:1343-1350) runs `generateSndE2EParams`, `pqX3dhSnd` against Alice's Rcv parameters, `initSndRatchet`, `createSndRatchet`.
- O6. `secureConfirmQueue` (Agent.hs:1396, 3747-3765): `agentSecureSndQueue` secures Q_A with `SKEY` (Agent.hs:3749); `mkAgentConfirmation` (Agent.hs:3780-3785) calls `createReplyQueue` to create Bob's receive queue Q_B and returns `AgentConnInfoReply (Q_B :| []) bobInfo`; `mkConfirmation` ratchet-encrypts it and wraps `AgentConfirmation {e2eEncryption_ = Just bobSndParams, encConnInfo}`; `sendConfirmation` sends it to Q_A. This is confirmation #1.
Requester side, receiving confirmation #1 on Q_A:
- R4. dispatch (Agent.hs:3181-3183): state `(Nothing, Just e2ePubKey)`, `AgentConfirmation` -> `smpConfirmation`.
- R5. `smpConfirmation`, initiating-party branch `RcvConnection … Just e2eEncryption` (Agent.hs:3405-3444): `getRatchetX3dhKeys`, `pqX3dhRcv` (Agent.hs:3408), `initRcvRatchet` (Agent.hs:3411), `createRatchet` (Agent.hs:3436), `setRcvQueueConfirmedE2E` (Agent.hs:3440); decrypts `AgentConnInfoReply` (Agent.hs:3420); `processConf` emits `CONF` (Agent.hs:3444).
- R6. `allowConnection'` (Agent.hs:1467-1474): `acceptConfirmation`, then `ICAllowSecure` secures Q_A with Bob's sender key.
- R7. `connectReplyQueues` (Agent.hs:3724-3737): `upgradeConn` creates Alice's send queue to Q_B; `agentSecureSndQueue` secures Q_B; `enqueueConfirmation … Nothing` (Agent.hs:3733) stores `AgentConnInfo aliceInfo` and sends `AgentConfirmation {e2eEncryption_ = Nothing, encConnInfo}` to Q_B. This is confirmation #2.
Owner side, receiving confirmation #2 on Q_B:
- O7. dispatch (Agent.hs:3182): `AgentConfirmation` -> `smpConfirmation`.
- O8. `smpConfirmation`, accepting-party branch `DuplexConnection … Nothing` (Agent.hs:3447-3462): `agentRatchetDecrypt` with the established ratchet; `AgentConnInfo` -> `INFO` (Agent.hs:3452); `ICDuplexSecure` or `CON`.
Completion is direct `CON` on `senderCanSecure` (SKEY) messaging-mode queues (the sender on `AgentConnInfo`, Agent.hs:2252; the receiver with no `senderKey`, Agent.hs:3459-3461); the separate `HELLO` via `helloMsg` (Agent.hs:3466) is the older non-`senderCanSecure` (duplexHandshake v2, in-band-securing) path.
## Part 2 - the DR-from-address handshake, mapped to Part 1
The address advertises Bob's Rcv X3DH parameters in link data (Part 3). Alice, when the address advertises them and versions are compatible, takes the joiner role; Bob takes the initiator role.
Requester side - a new branch in `joinConnSrv … CRContactUri`, taken when the passed `Maybe AddressRatchetKeys` is present (the caller's plan-time `LGET`):
- R2'. Replaces R2/R3. Read the passed bundle - `ratchetKeyId` and `e2eParams :: RcvE2ERatchetParamsUri 'C.X448` - and negotiate the concrete version with `compatibleVersion` against the client e2e range, as `compatibleInvitationUri` does (Agent.hs:1362-1368). Create the receive queue Q_A subscribed (`newRcvQueue` with `subMode`), messaging mode so Bob can secure it. Choose the requester's KEM with `replyKEM_ v ownerKem_ pqSup` (Ratchet.hs:839): if the bundle advertises a KEM (owner `IKUsePQ`) the requester `AcceptKEM` - a **double KEM**: it both encapsulates to the address KEM (ciphertext) and includes its own new KEM public key (`generateSndE2EParams``sntrup761Enc` + a fresh keypair, Ratchet.hs:433-435), so PQ is bidirectional from message 1; if the bundle has no KEM and the requester wants PQ, it `ProposeKEM` (its own key only, PQ from message 2 if the owner supports it). Run `generateSndE2EParams g v (replyKEM_ …)`, `pqX3dhSnd` against the negotiated parameters, `initSndRatchet`, `createSndRatchet` - the body of `createRatchet_` (Agent.hs:1343-1350), with parameters from the passed bundle rather than a received invitation.
- R3'. Build `AgentConfirmation {e2eEncryption_ = Just aliceSndParams, ratchetKeyId = Just ratchetKeyId, encConnInfo = ratchetEncrypt(AgentConnInfoReply (Q_A :| []) aliceProfile)}` - the `mkAgentConfirmation`/`mkConfirmation` bodies (Agent.hs:3780-3765) with the reply queue being Alice's own Q_A. Send it to the address queue unauthenticated with `agentCbEncryptOnce`, one-shot (as `sendInvitation` sends, Agent/Client.hs:1929-1934) - **synchronous**, with the same send-failure UX as today's classic contact join. Nothing is stored: a retry (chat re-invokes the join → `mkJoinInvitation` reuses Q_A + keys, 1418) re-builds the confirmation, advancing the send ratchet, and the owner absorbs the advance - a **failed send** is skipped when the owner establishes the ratchet (`maxSkip = 512`, Ratchet.hs:988), and a **lost reply** carries the current content and updates the owner's request by `XContactId` (ContactRequest.hs:99-101, 269); both testable. The requester does **not** SKEY the address (`QMContact`, not `senderCanSecure`); rotation is handled because the passed params are the current advertised keys. **Alice's profile is now inside `encConnInfo`, under the ratchet.** Alice's connection is `RcvConnection` (Q_A) with a send ratchet, until she receives Q_B. This "New `RcvConnection` + `ratchets` row" is a new state (today a New `RcvConnection` holds x3dh keys but no ratchet - the classic initiator builds the ratchet only at R5, `createRatchet` Agent.hs:3436), and it composes: connection type is derived from queue rows alone while the `ratchets` table is keyed independently by `conn_id`, so subscription (Agent.hs:1551), `connectionStats` (2658), and `allowConnectionAsync'` (888) never read the ratchet for a `RcvConnection`; the only handshake reader on it is `smpConfirmation` (R5').
### Deferred (future work): async delivery + connect UX
The synchronous send above fails in the user's face on a lost reply (the same wart as today's classic contact join), even though the request may have been delivered. Making it async is a separate, later change, not part of this DR work:
- Delivery cannot use the message-delivery worker: a `SndQueue` is unique per `(host, port, snd_id)` and belongs to one connection (schema PK), while a contact address is one queue that many connections send to, so no per-connection SndQueue to it can exist. It would go through the **async command worker**, keyed by `(connId, server)` (`getAsyncCmdWorker`, Agent.hs:1856-1858), which already retries the `JOIN` command (`tryMoveableCommand``retrySndOp`, 2016-2024); each retry re-runs `joinConnSrv` (re-build + ratchet advance, which the owner absorbs - above), so nothing is stored. (`joinConnSrvAsync` for `CRContactUri` is `CMD PROHIBITED` today, Agent.hs:1452, and the `JOIN` handler falls back to sync `joinConnSrv`, 1899-1902; the `TBC` at Agent.hs:1897 is about async *receive*-queue creation - Q_A - and is orthogonal.)
- The async join returns "connecting" early and completes via the events chat already handles (`joinContact` sets `ConnJoined`; the DR requester emits `CONF` in R5' and the chat allows it, exactly as the classic contact requester, driving msg 3 → `CON`; a permanent send failure still surfaces as `ERR → ConnFailed`).
- This needs a chat change: the join API takes a `CreatedConnLink` (full + short link), not the bare `ConnectionRequestUri` it takes today, so the agent can LGET-gate on the owner's server (a real reachability check) and verify the fetched `linkConnReq` equals the passed full link before reporting success. Used only for DR addresses (link data advertises `ratchetKeys`); old / non-DR addresses stay on the current sync path.
Owner side - a new dispatch branch and a new receive handler:
- O1'. In `processClientMsg` (Agent.hs:3176-3187), add a branch in state `(Nothing, Just e2ePubKey)`: an `AgentConfirmation` with `ratchetKeyId = Just _` **and** `e2eEncryption_ = Just _` on a `ContactConnection` -> `smpAddressConfirmation` (new). A `ratchetKeyId` without `e2eEncryption_` is ignored (it does not match this branch and falls through as a non-DR confirmation). It must be placed **before** the existing `(PHEmpty, AgentConfirmation) | senderCanSecure queueMode` case (Agent.hs:3182-3184), because a contact-address queue is `QMContact` (not `senderCanSecure`) and would otherwise fall into `prohibited "handshake: missing sender key"` (Agent.hs:3184). The address queue's `e2eDhSecret` stays `Nothing` (it is never set for a contact address - `smpInvitation` does not set it, Agent.hs:3609-3622), so every request is decrypted with its own ephemeral key via this `(Nothing, Just e2ePubKey)` path.
- O2'. `smpAddressConfirmation` (new, modeled on `smpConfirmation` initiating branch, Agent.hs:3405-3444): select the private triple `(pk1, pk2, pKem)` by `ratchetKeyId` from `address_ratchet_keys`; `pqX3dhRcv pk1 pk2 pKem aliceSndParams`; `initRcvRatchet` with the address connection's stored `PQSupport` (`connPQEncryption` of the address `InitialKeys` - `On` for `IKUsePQ` and `IKPQOn`, `Off` for `IKPQOff`; this is what lets `IKPQOn` accept the requester's proposed KEM), combined with version compatibility as `smpConfirmation` derives `pqSupport'` (Agent.hs:3410); `rcDecrypt` of `encConnInfo` performs the first ratchet step, giving the ratchet its send side too (as it does for the initiator today), so the owner can later reply. Parse `AgentConnInfoReply (Q_A :| []) aliceProfile`. Store the request with `createInvitation` on the address connection (`contact_conn_id`), exactly as a classic invitation - except the request value is the `CRConfirmation` variant (Part 3) carrying the post-decrypt ratchet state and Q_A, and `recipient_conn_info` is `aliceProfile` - so **no connection or `ratchets` row is created at receive**, as with a classic invitation. Emit `REQ` with the `invitation_id`. A resend is not deduplicated: like a resent classic invitation it produces another `REQ` (the connect-UX fix for that is separate chat work). An unknown or expired `ratchetKeyId`, or a decryption failure: discard and acknowledge, as an undecryptable message is dropped today. This establishes ratchet state on unauthenticated input before the user accepts - see "Receive-time establishment, state, and abuse".
- O3'. `acceptContact'` for a DR request - a new branch that continues the ratchet instead of `joinConn`. `getInvitation` returns the request; its `CRConfirmation` variant gives the stored ratchet state and Q_A. Create the connection now (as `joinConn` does for a classic invitation) and `createRatchet` (AgentStore.hs:1419) from the stored ratchet state. Reuse `mkAgentConfirmation` (Agent.hs:3780-3785) to create Bob's receive queue Q_B and return `AgentConnInfoReply (Q_B :| []) bobInfo`; create Bob's send queue to Q_A (`newSndQueue`, generating Bob's own sender key) and secure Q_A with `SKEY` using that key (`agentSecureSndQueue`, valid because Q_A is messaging mode) - the securing key is Bob's own, not taken from Alice's message; send the response to Q_A as `AgentConfirmation {e2eEncryption_ = Nothing, ratchetKeyId = Nothing, encConnInfo = ratchetEncrypt(AgentConnInfoReply (Q_B :| []) bobInfo)}` via `sendConfirmation` (`agentCbEncrypt` over Bob's send queue to Q_A, `PHEmpty` because Q_A is `senderCanSecure`) - exactly the current contact msg 2 path (Client.hs:1916), not `agentCbEncryptOnce`. The reply content is `AgentConnInfoReply`, not `AgentConnInfo`: it takes the `mkAgentConfirmation` path with `e2eEncryption_ = Nothing`, not the `enqueueConfirmation` path (which produces `AgentConnInfo`, Agent.hs:3789). `rejectContact'` deletes the `conn_invitations` row (the current behaviour), discarding the inline ratchet; no connection was created, so there is nothing else to clean up.
Requester side, receiving the response on Q_A:
- R5'. `smpConfirmation` needs a new branch `RcvConnection … Nothing` (today only `RcvConnection … Just` and `DuplexConnection … Nothing` exist, Agent.hs:3403-3447). It looks up the ratchet first (`getRatchet`) and, if there is none, falls through to `prohibited "conf: incorrect state"` - so a classic initiator (a New `RcvConnection` with x3dh keys but no ratchet) that receives a stray `Nothing`-confirmation keeps today's exact outcome; only a DR requester, which holds a send ratchet, takes the new path. Alice already holds the send ratchet, so `rcDecrypt` advances it and creates the receive side; parse `AgentConnInfoReply (Q_B :| []) bobInfo`. **This mirrors the classic contact requester exactly**: `setRcvQueueConfirmedE2E` on Q_A, `createRatchet` the advanced ratchet, store the reply as a `NewConfirmation`, and emit **`CONF`** - the app then calls `allowConnection'` (supplying `ownConnInfo` for msg 3), which drives `connectReplyQueues` (create Alice's send queue to Q_B, `SKEY`, upgrade to `DuplexConnection`, `enqueueConfirmation` the `AgentConnInfo` msg 3). Because Q_B is sender-securable, sending `AgentConnInfo` completes Alice with `CON` (Agent.hs:2252) - no `HELLO`. The only difference from the classic requester is that the ratchet is pre-built (from R2') rather than built from Bob's Snd params here, so there is no `CONF`-less auto-completion and no separate storage of Alice's own info.
- R6'/completion. Unchanged from the current contact handshake, and modern (no `HELLO`). The exchange is three agent↔agent wire messages - Alice → address queue (msg 1), Bob → Q_A (msg 2, an `AgentConfirmation` carrying `AgentConnInfoReply` with Q_B), Alice → Q_B (msg 3, an `AgentConfirmation` carrying `AgentConnInfo`) - the same shape as the current contact flow, where msg 1 was `AgentInvitation`; here it is the ratchet-establishing `AgentConfirmation`. (`CON` is not a wire message - it is the agent→app event; `HELLO` and `AgentConnInfo` are the wire messages.) `HELLO` belongs to the older non-`senderCanSecure` path (duplexHandshake v2, before SKEY): there the confirmation secures the queue in-band (`PHConfirmation` carries the sender key, Client.hs:1918) and the receiver replies with `HELLO` (`ICDuplexSecure``enqueueDuplexHello`, Agent.hs:3457-3458). Both Q_A and Q_B here are messaging-mode - Q_A by R2', Q_B via `createReplyQueue``SCMInvitation``QMMessaging` (Agent.hs:1233,1458,3783) - so the sender secures with SKEY and sends `PHEmpty` (Client.hs:1918), the dispatch takes the `senderCanSecure` branch (Agent.hs:3182-3184), and each agent raises the `CON` app event locally off msg 3 - Bob on receiving it (`senderKey = Nothing`, Agent.hs:3459-3461), Alice on sending it (Agent.hs:2252) - with no separate `HELLO` wire message. (msg 2's `AgentConnInfoReply` only sets Q_A `Confirmed`, Agent.hs:2254.) Invitations are two messages because the initiator's queue is already in the link; a contact address needs three because Bob's receive queue Q_B is only delivered in msg 2. The third message no longer has a ratchet role: Bob's X3DH params are pre-published, so the agreement is complete once Bob receives msg 1 (in the current flow Bob's Snd params instead arrive in msg 2). msg 2 and msg 3 are queue setup - msg 2 delivers Q_B, msg 3 secures Q_B so Alice can send to Bob and signals Bob's `CON`; neither negotiates the ratchet. A one-directional exchange (the RPC) needs no Q_B and is two messages.
Net code touch points: `joinConnSrv` (new requester branch), `processClientMsg` (new owner dispatch), `smpConfirmation` (new `RcvConnection … Nothing` branch and `AgentConnInfoReply` acceptance), `acceptContact'` (new continue-ratchet branch), a new `smpAddressConfirmation` reusing `createInvitation`/`getInvitation` with the sum request value, and the link data and storage of Part 3-4. `rejectContact'` is unchanged (it deletes the `conn_invitations` row either way).
### Receive-time establishment, state, and abuse
This is the substantive departure from the current flow. Today `smpInvitation` creates only a lightweight `NewInvitation` and emits `REQ` (Agent.hs:3618-3621); no connection or ratchet exists until the user accepts. For DR the request is under the ratchet, so to show the requester's profile in `REQ` the owner must decrypt it, which means establishing the ratchet at **receive**, before accept.
Design decision (Q1): decrypt at receive. Both use cases need the request content at `REQ` - a person decides to accept from the profile, and a service bot needs the request payload to act. Deferring decryption to accept would make `REQ` contentless and does not fit the service case, so it is not done.
Consequences:
- No connection is created at receive, exactly as for a classic invitation. O2' stores the request with `createInvitation` on the address connection; the post-decrypt ratchet state and Q_A live inline in the `CRConfirmation` request value (`cr_invitation`). O3' (accept) creates the connection, `createRatchet` from the stored state, and adds Bob's queues, becoming a `DuplexConnection`; `rejectContact'` deletes the `conn_invitations` row.
- Per incoming `AgentConfirmation` the owner does one `pqX3dhRcv` (three DH plus, with PQ, one `sntrup761` decapsulation) and one `rcDecrypt`, on unauthenticated input, and writes one `conn_invitations` row - more CPU than the current `NewInvitation`, the same order of state (no connection, no `ratchets` row until accept).
Abuse (Q2): a contact address already accepts and processes unauthenticated invitations today, so this is a degree-worse version of an existing surface, not a new class. It is bounded by the address queue quota (an attacker fills it, the owner drains and acknowledges) and, optionally, by basic auth on the address (already supported for contact addresses, `optBasicAuth`). The per-request state is a single `conn_invitations` row - the same class as a classic contact request - so it is subject to the same limits and lifecycle, with no DR-specific dedup or TTL. Proof-of-work or a stricter gate can be added later; it is out of scope here and noted as a follow-up.
`acceptContact'`/`rejectContact'` keep taking the `invitation_id` from `REQ` unchanged; the only difference is that `getInvitation` returns a request that is either a `CRInvitation` URI (current `joinConn` path, O3-O6) or a `CRConfirmation` (continue-ratchet path, O3'). Nothing in the `REQ`/accept/reject flow or the chat client changes - the change is contained in the agent.
### The four communication layers, per message (verified against code)
Layers, outermost (server-visible) first:
- **L1 `ClientMsgEnvelope`** (Protocol.hs:1089), `PubHeader {phVersion, phE2ePubDhKey :: Maybe PublicKeyX25519}` (1096) - **this is where per-queue encryption is agreed** (not L2). `phE2ePubDhKey` is the sender's e2e DH public key; the recipient combines it with the queue's e2e private key: `(e2eDhSecret, e2ePubKey_) -> (Nothing, Just e2ePubKey) -> e2eDh = dh' e2ePubKey e2ePrivKey` (Agent.hs:3172-3178). `agentCbEncryptOnce` (Client.hs:2214) puts a **fresh ephemeral** pubkey (generated 2217, set 2223) - used when the sender has no send queue (the address queue), whose `e2eDhSecret` stays `Nothing`, so it decrypts every message with the per-message ephemeral. `agentCbEncrypt` (Client.hs:2203) puts the **send queue's persistent** e2e pubkey (`Just` on a confirmation, 2210); the recipient stores the secret via `setRcvQueueConfirmedE2E`, and *later* messages send `phE2ePubDhKey = Nothing` (`sendAgentMessage`, 2080).
- **L2 `ClientMessage PrivHeader`** (Protocol.hs:1113), `PrivHeader = PHConfirmation APublicAuthKey | PHEmpty` (1115) - **queue securing / authorization, not encryption**. `PHConfirmation` carries the sender's AUTH key for in-band securing (v2, non-`senderCanSecure`); `PHEmpty` when the sender secured the queue with SKEY out-of-band. `PHEmpty` on every message here is about securing, and says nothing about encryption (that is L1). Set in `sendConfirmation` (Client.hs:1918), `sendInvitation` (1934), `sendAgentMessage` (2079).
- **L3 `AgentMsgEnvelope`** (Agent/Protocol.hs:829, encoding 851) - outside the ratchet. `AgentConfirmation` ('C') carries `e2eEncryption_` (Snd X3DH params, agrees DR) + `encConnInfo`; `AgentInvitation` ('I') carries `connReq` (Rcv X3DH params) + plaintext `connInfo` (no DR); `AgentMsgEnvelope` ('M') carries `encAgentMessage`.
- **L4 `AgentMessage`** (Agent/Protocol.hs:883, encoding 893) - inside the ratchet. `AgentConnInfo` ('I'), `AgentConnInfoReply` ('D', reply queues + info), `AgentMessage APrivHeader AMessage` ('M'; `AMessage` includes `HELLO`, Agent/Protocol.hs:1018-1020). **Absent when L3 is `AgentInvitation`** (that profile is per-queue-only - the gap this plan closes).
Send routing: msg 1 (to address) → `sendInvitation` today / a new `agentCbEncryptOnce` confirmation send for DR; msg 2 → `secureConfirmQueue``sendConfirmation` (Agent.hs:3747); msg 3 → `connectReplyQueues``enqueueConfirmation` → delivery worker `AM_CONN_INFO``sendConfirmation` (Agent.hs:3733,3789,2183). `AM_CONN_INFO`/`AM_CONN_INFO_REPLY` both go through `sendConfirmation` (2183-2184); other `AMessage`s go through `sendAgentMessage` wrapping `AgentMsgEnvelope` 'M' (2192-2193).
Current contact handshake (address does **not** advertise DR):
| msg | L1 `PubHeader.phE2ePubDhKey` (per-queue enc) | L2 `PrivHeader` (securing) | L3 `AgentMsgEnvelope` | L4 `AgentMessage` |
|---|---|---|---|---|
| 1 Alice→addr | `Just` fresh ephemeral, `agentCbEncryptOnce` (Client.hs:1933,2223) | `PHEmpty` (1934) | `AgentInvitation` {connReq = Alice Rcv params, connInfo = profile} (Client.hs:1932) | — none (profile per-queue only) |
| 2 Bob→Q_A | `Just` Bob's send-queue e2e pubkey, `agentCbEncrypt` (1920,2210) | `PHEmpty` [`senderCanSecure`] (1918) | `AgentConfirmation` {e2eEncryption_ = **Just Bob Snd params**, encConnInfo} (Agent.hs:3765) | `AgentConnInfoReply` (Q_B) bobInfo, DR-enc (Agent.hs:3785) |
| 3 Alice→Q_B | `Just` Alice's send-queue e2e pubkey, `agentCbEncrypt` (1920,2210) | `PHEmpty` [`senderCanSecure`] (1918) | `AgentConfirmation` {e2eEncryption_ = **Nothing**, encConnInfo} (Agent.hs:3802) | `AgentConnInfo` aliceInfo, DR-enc (Agent.hs:3789) |
New DR handshake (address advertises DR):
| msg | L1 `PubHeader.phE2ePubDhKey` (per-queue enc) | L2 `PrivHeader` (securing) | L3 `AgentMsgEnvelope` | L4 `AgentMessage` |
|---|---|---|---|---|
| 1 Alice→addr | `Just` fresh ephemeral, `agentCbEncryptOnce` [same] | `PHEmpty` [same] | **`AgentConfirmation`** {e2eEncryption_ = **Just Alice Snd params**, **ratchetKeyId = Just**, encConnInfo} [was `AgentInvitation`] | **`AgentConnInfoReply`** (Q_A) aliceProfile, **DR-enc** [was plaintext connInfo] |
| 2 Bob→Q_A | `Just` Bob's send-queue e2e pubkey, `agentCbEncrypt` [same] | `PHEmpty` [same] | `AgentConfirmation` {e2eEncryption_ = **Nothing**, ratchetKeyId = Nothing, encConnInfo} [was Just Bob Snd params] | `AgentConnInfoReply` (Q_B) bobInfo, DR-enc [same] |
| 3 Alice→Q_B | `Just` Alice's send-queue e2e pubkey, `agentCbEncrypt` [same] | `PHEmpty` [same] | `AgentConfirmation` {e2eEncryption_ = Nothing, encConnInfo} [same] | `AgentConnInfo` aliceInfo, DR-enc [same] |
Net difference: **only msg 1 and msg 2's L3/L4 change.** msg 1's L3 becomes `AgentConfirmation` (was `AgentInvitation`) carrying Alice's Snd params + `ratchetKeyId`, and the profile moves from plaintext L3 to DR-encrypted L4 (`AgentConnInfoReply`) - the whole point of the change. msg 2 drops `e2eEncryption_` (Bob no longer sends Snd params - the ratchet is agreed from msg 1). msg 3 is unchanged. L1 (per-queue encryption - each queue agrees its own secret via the sender's e2e pubkey in the `PubHeader` on the first message to it) and L2 (securing, `PHEmpty` because SKEY is used) are unchanged throughout; the DR change is entirely at L3/L4. The only new send code is msg 1 (an `AgentConfirmation` fired to the address with `agentCbEncryptOnce`, like `sendInvitation` but with a confirmation envelope).
## Part 3 - types and link data
### Fixed data - unchanged
`FixedLinkData` (Protocol.hs:1824) is not touched. The double-ratchet keys go entirely in mutable data, so an existing address advertises them without a new link (the fixed data is hash-committed and cannot change). Fixed data keeps only `agentVRange`, `rootKey`, `linkConnReq`, `linkEntityId`.
### Mutable data - ratchet keys bundle
Appended to `UserContactData` (Protocol.hs:1840); the encoding stops at a trailing tail (Protocol.hs:1981), so earlier versions ignore it:
```haskell
newtype RatchetKeyId = RatchetKeyId ByteString -- opaque short id; one Encoding instance, shared below
data AddressRatchetKeys = AddressRatchetKeys
{ ratchetKeyId :: RatchetKeyId, -- identifies this bundle; changes on rotation, echoed in the request
e2eParams :: CR.RcvE2ERatchetParamsUri 'C.X448 -- version range + both X3DH keys + optional KEM
}
instance Encoding AddressRatchetKeys where ... -- the key-bundle instance; both fields required
data UserContactData = UserContactData
{ direct :: Bool, owners :: [OwnerAuth], relays :: [ConnShortLink 'CMContact],
userData :: UserLinkData,
ratchetKeys :: Maybe AddressRatchetKeys -- whole bundle optional, one Encoding instance
}
```
`e2eParams` is the existing `RcvE2ERatchetParamsUri 'C.X448` (`E2ERatchetParamsUri VersionRangeE2E k1 k2 (Maybe (RKEMParams s))`, Ratchet.hs:282-286) - the same type a `CRInvitationUri` advertises - with `StrEncoding`/`Encoding` already defined (Ratchet.hs:302-374). There is no bespoke key type and no reconstruction: the requester negotiates the concrete version with `compatibleVersion` against its own e2e range, exactly as `compatibleInvitationUri` does for an invitation (Agent.hs:1362-1368), giving `RcvE2ERatchetParams` for `pqX3dhSnd`. The KEM is optional: `Nothing` gives an X448-only ratchet (as when `PQSupport` is off), `Just` a hybrid one, matching `generateRcvE2EParams`'s `PQSupport` gate (Ratchet.hs:439-445).
The address-creation parameter is `InitialKeys` (Ratchet.hs:864) - the same 3-way choice as invitations, not a bare `PQSupport`. Currently `IKUsePQ` is prohibited for `SCMContact` (Agent.hs:990,1198) because a contact address carries no owner keys; this change lifts that prohibition. The bundle plays the published-contact-request role, so its KEM follows `initialPQEncryption False pqInitKeys` (Ratchet.hs:882) - exactly as the requester's contact request does today (Agent.hs:1422):
- `IKUsePQ` - the bundle advertises the KEM; the requester encapsulates to it, so PQ from message 1.
- `IKPQOn` (`IKLinkPQ PQSupportOn`) - the bundle is X448-only (no KEM advertised), but the owner's ratchet supports PQ (`connPQEncryption` = On, Ratchet.hs:888); the requester proposes its own KEM (R2'), so PQ from message 2.
- `IKPQOff` (`IKLinkPQ PQSupportOff`) - X448-only, and the owner's ratchet does not support PQ even if the requester proposes it.
Advertising the KEM adds ~1158 B to the rotated, widely-fetched link data, which is why `IKPQOn` exists (PQ one round later, without the size cost). The owner generates the bundle with `generateRcvE2EParams g v (initialPQEncryption False pqInitKeys)` (Ratchet.hs:439), stores the private triple `(pk1, pk2, pKem)` (Part 4), and advertises `e2eParams` by wrapping the public `E2ERatchetParams` in the address's e2e version range (`toVersionRangeT`; or `mkRcvE2ERatchetParams` from the stored privates, Ratchet.hs:412) - the same private-key shape `createRatchetX3dhKeys`/`getRatchetX3dhKeys` already store (AgentStore.hs:1362-1367). `ratchetKeys` is set by the agent when it signs mutable link data (`Crypto.ShortLink.encodeSignUserData`), not by the application.
### Authentication of the advertised keys
No signature is added on the keys: the mutable link data already signs them. `decryptLinkData` (Crypto/ShortLink.hs:106-114) verifies `sig2` over the mutable `UserContactData` by `rootKey`, so `ratchetKeys` is root-signed. This is the X3DH anti-substitution property: an SMP server cannot substitute the keys without forging the root signature. The signer is the root Ed25519 key (the address's signing identity); the X3DH keys are separate DH keys (X448, which cannot sign). A single owner signs address data ("we don't use multiple owners"), so the root signature alone is sufficient - no per-key signature. A malicious server can still serve an older but validly-signed `UserContactData` (rollback to a retired bundle); this is bounded by the retention window and by the ratchet advancing after the first message, and a signature does not prevent it. Inline ratchet params in a `CRInvitationUri` contact request are not in signed link data and remain unsigned - a separate change, out of scope here.
### Request envelope
`AgentConfirmation` (Protocol.hs:830-834) gains an optional `ratchetKeyId` - the `ratchetKeyId` of the `AddressRatchetKeys` bundle the requester used, so the owner selects the matching private keys:
```haskell
AgentConfirmation
{ agentVersion :: VersionSMPA,
e2eEncryption_ :: Maybe (SndE2ERatchetParams 'C.X448), -- reused: Alice's Snd params in DR msg 1
ratchetKeyId :: Maybe RatchetKeyId, -- selects the owner's key generation
encConnInfo :: ByteString
}
```
`ratchetKeyId` is a separate optional selector (the shared `RatchetKeyId` newtype), not the bundle - the owner already holds the published public bundle and looks up its private keys by this id. It reuses the existing `e2eEncryption_` for Alice's Snd params rather than a new combined bundle; the minor cost is two correlated `Maybe`s (`ratchetKeyId = Just` is only meaningful with `e2eEncryption_ = Just`). **A `ratchetKeyId` with `e2eEncryption_ = Nothing` is ignored** - O2' requires both (there are no Snd params to run `pqX3dhRcv`), so such a message falls through to the current dispatch as if it had no `ratchetKeyId`.
Encoding (extends Protocol.hs:853-866): from `addressDRVersion`, `smpEncode (agentVersion, 'C', e2eEncryption_, ratchetKeyId, Tail encConnInfo)`; `ratchetKeyId` is `Just` for an address-DR confirmation and `Nothing` for the current joiner-to-initiator and initiator-to-joiner confirmations; earlier versions omit the field entirely and use `smpEncode (agentVersion, 'C', e2eEncryption_, Tail encConnInfo)`. Parsing gates the field on `agentVersion`. `CRInvitationUri` is unchanged - a connection request URI holds Rcv parameters and must not hold Snd parameters.
### Stored request - the invitation record
The `conn_invitations` record stays; only the type of the stored request widens. From chat's point of view a DR request is still an invitation - it "contains a confirmation" instead of an invitation URI - so `REQ`, `acceptContact'`/`rejectContact'`, and the chat side are unchanged; the change is contained in the agent. The `NewInvitation`/`Invitation` request field (`cr_invitation`, stays `NOT NULL`) becomes a sum:
```haskell
data ContactRequest
= CRInvitation (ConnectionRequestUri 'CMInvitation) -- classic: joinConn on accept (O3-O6)
| CRConfirmation DRRequest -- DR: continue the ratchet on accept (O3')
data DRRequest = DRRequest
{ drRatchet :: RatchetX448, -- post-decrypt receiving ratchet (with send side), stored inline
drReplyQueue :: SMPQueueInfo, -- Q_A, where the owner replies
drAgentVersion :: VersionSMPA, -- negotiated at receive; needed to build the connection shell at accept
drPQSupport :: PQSupport -- the address's PQ setting for this connection
}
```
`recipient_conn_info` holds the profile in both cases. `getInvitation`/`createInvitation` carry `ContactRequest`; `acceptContact'` branches on the constructor. There is no dedup column: a resent request produces another `REQ`, exactly as a resent classic invitation does.
`drAgentVersion`/`drPQSupport` are stored because the accept flow creates the connection **shell** through `newConnToAccept``newConnToJoin` (via `prepareConnectionToAccept`, called by chat's sync accept before `acceptContact'`, Internal.hs:914,925) and `newConnToJoin` today derives `connAgentVersion`/`pqSupport` from the `ConnectionRequestUri` (Agent.hs:1277-1293); a `CRConfirmation` has no URI, so the values negotiated at receive (O2') are stored and used to build the shell.
Three readers of the widened `connReq` field (all via `getInvitation`) branch on the constructor:
- `acceptContact'` (Agent.hs:1479, sync): `CRInvitation cr``joinConn … cr` (classic, unchanged); `CRConfirmation dr` → the O3' continue-ratchet path.
- `newConnToAccept` (Agent.hs:1296, via `prepareConnectionToAccept`): `CRInvitation cr``newConnToJoin … cr` (unchanged); `CRConfirmation dr` → create the `NewConnection` shell from `drAgentVersion`/`drPQSupport` (`createNewConn`, generating the connId).
- `acceptContactAsync'` (Agent.hs:900): `CRInvitation cr``joinConnAsync … cr` (unchanged); `CRConfirmation _``throwE $ CMD PROHIBITED` (async DR accept is deferred; DR requests accept synchronously). Chat's REQ/accept is unaffected either way - it only ever passes `invId`, never the `ContactRequest`, which stays internal to the agent.
Storage: `cr_invitation`'s `ToField`/`FromField` encode `CRInvitation` as the legacy `strEncode` URI (unchanged from before, so the format is downgrade-safe) and `CRConfirmation` as JSON (`J.encode` of `DRRequest`). `FromField` peeks the first byte: `{` → JSON `CRConfirmation`, else `strDecode``CRInvitation` (a URI never starts with `{`). `DRRequest` uses manual `ToJSON`/`FromJSON` (extensible), and `SMPQueueInfo` gets a base64 `StrEncoding` + JSON to sit inside it. `smpInvitation` (Agent.hs:3618) wraps its `connReq` in `CRInvitation`; `smpAddressConfirmation` (O2') writes `CRConfirmation`.
## Part 4 - key rotation (client-driven)
Rotation is independent of the handshake above and is driven by the client app, not the agent. The agent never rotates on its own - it lacks the app's intent and the mutable link data (profile/badge and other short-link data). The app rotates by calling `setConnShortLink` with the rotate flag; the whole ratchet-keys bundle - both X448 keys and the KEM - is generated fresh each time.
### Schema
```sql
-- one row per ratchet-keys generation for an address; the current generation plus the most recent retained ones.
-- private side of the advertised RcvE2ERatchetParamsUri - same shape as the ratchets x3dh
-- columns and createRatchetX3dhKeys (AgentStore.hs).
CREATE TABLE address_ratchet_keys(
address_ratchet_key_id INTEGER PRIMARY KEY AUTOINCREMENT,
conn_id BLOB NOT NULL REFERENCES connections ON DELETE CASCADE,
ratchet_key_id BLOB NOT NULL, -- the published id echoed by requests
x3dh_priv_key_1 BLOB NOT NULL, -- X448
x3dh_priv_key_2 BLOB NOT NULL, -- X448
pq_priv_kem BLOB, -- RcvPrivRKEMParams (sntrup761 keypair); NULL when PQ is off for this address
created_at TEXT NOT NULL DEFAULT(datetime('now'))
);
CREATE UNIQUE INDEX idx_address_ratchet_keys ON address_ratchet_keys(conn_id, ratchet_key_id);
-- a DR request stays in conn_invitations with NO schema change: cr_invitation now holds a ContactRequest
-- sum (an invitation URI or a confirmation carrying the post-decrypt ratchet + reply queue), so it stays
-- NOT NULL - no nullable change, no new column on conn_invitations, no new table for the request.
```
`cr_invitation` stays `NOT NULL` - only its decoded value gains a variant (Part 3), so the invitations flow, `REQ`, and chat are unchanged; the only new storage is the `address_ratchet_keys` table. The link signing key is already on the address queue (`rcv_queues.link_priv_sig_key`, M20250322), so nothing is added there - rotation and retrofit re-sign mutable data with it. PostgreSQL mirrors this. Migration `M20260712_address_dr`.
### Rotation logic
The app rotates by calling `setConnShortLink` with the rotate flag; there is no automatic, agent-driven rotation. On rotation:
1. `generateRcvE2EParams` for a fresh generation - two X448 keys, and an sntrup761 keypair only if PQ is on for this address - with a fresh `ratchetKeyId`.
2. Recompute mutable link data with the new `AddressRatchetKeys` (the public `e2eParams`), re-sign with the root key (`encodeSignUserData`, key from `rcv_queues.link_priv_sig_key`), and `LSET` it to the address queue (`setConnShortLink` path).
3. Insert the new `address_ratchet_keys` row (`x3dh_priv_key_1`, `x3dh_priv_key_2`, `pq_priv_kem`).
### Retention
Retention is count-based, not time-based: on each rotation `deleteOldAddressRatchetKeys` keeps the newest `keepAddressKeys` generations per address (default 3, ordered by `address_ratchet_key_id`) and deletes older ones. There is no `retired_at` column, no time window, and no `cleanupManager` step. A request that used a recently-retired bundle still decrypts while that generation is retained; how long a recorded first message stays decryptable after a compromise of the current private keys is therefore bounded by the retained-generation count and the app's rotation cadence (both app-controlled). Unaccepted DR request rows in `conn_invitations` are handled exactly like unaccepted classic invitation requests - no DR-specific cleanup (a DR request is one `conn_invitations` row, the same class of state as a classic contact request).
## Part 5 - backward compatibility
- A requester older than `addressDRVersion`, or an address without `ratchetKeys`, uses R2/R3 (`AgentInvitation`); the owner uses O1-O8. Unchanged.
- The owner dispatches on the envelope: `AgentInvitation` -> `smpInvitation` (current); `AgentConfirmation` with `ratchetKeyId` on a `ContactConnection` -> `smpAddressConfirmation` (new). Both coexist.
- `AgentConfirmation` without `ratchetKeyId` remains the current confirmation on established connections.
- An existing address gains `ratchetKeys` via a new agent API (e.g. `updateContactAddressLink`) that the app calls with the mutable link data (profile/badge and any other short-link data): the agent generates the DR bundle if absent (the first `address_ratchet_keys` row and its stored private keys), adds `ratchetKeys` to `UserContactData`, re-signs with `rcv_queues.link_priv_sig_key`, and `LSET`s it. **Only mutable data changes - the address (link) is unchanged**, because the keys are in mutable, not fixed, data. Requesters that fetch the updated data use DR; older ones still use `AgentInvitation`. The agent does not do this on its own (it lacks the profile and the user's intent); the app drives it, combined with the full→short address migration.
## Part 6 - tests
- Encoding roundtrips: `UserContactData` with and without `ratchetKeys`, and with the KEM present and absent; `AgentConfirmation` with and without `ratchetKeyId`, across versions.
- Address creation advertises `ratchetKeys` (the `RcvE2ERatchetParamsUri`); `decryptLinkData` (Crypto/ShortLink.hs:100) verifies signatures and the requester negotiates the advertised params to a concrete version, with and without the KEM.
- Both PQ modes: an address whose bundle carries a KEM gives a hybrid ratchet (`pqEncryption` on); one without gives an X448-only ratchet.
- End to end: a DR-advertising address; a new requester establishes the ratchet, sends its profile under it, owner emits `REQ`, accepts, both reach `CON`; assert the profile never travels under per-queue-only encryption; assert `pqEncryption` on.
- Rotation and retrofit: request against the current bundle; against a just-retired bundle within the window still decrypts; against a bundle past the window is discarded and the requester times out; an address that adds `ratchetKeys` via `LSET` is then reached by DR while an old requester still uses `AgentInvitation`.
- Backward compatibility: old requester against a DR address connects via `AgentInvitation`; new requester against a non-DR address falls back to `AgentInvitation`.
## Part 7 - phases
1. Link data: `AddressRatchetKeys` in `UserContactData` (reusing `RcvE2ERatchetParamsUri`), encoding, `encodeSignUserData`; `AgentConfirmation.ratchetKeyId`; address creation taking `InitialKeys` (lifting the `IKUsePQ`-for-`SCMContact` prohibition), generating (`generateRcvE2EParams`, KEM per `initialPQEncryption False`) and storing the first `address_ratchet_keys` row.
2. Handshake: thread the optional `Maybe AddressRatchetKeys` through `joinConnection`/`joinConn`/`joinConnSrv` (present → DR branch, absent → classic); requester R2'/R3' (synchronous one-shot send); owner O1'/O2'/O3' storing the DR request as a `conn_invitations` row whose request value is the `CRConfirmation` variant; `smpConfirmation` `RcvConnection … Nothing` branch and `AgentConnInfoReply` acceptance; end-to-end connection with the profile under the ratchet. Tests pass `AddressRatchetKeys` directly (chat wiring is later work).
3. Rotation and retrofit: schema migration, `rotateRatchetKeys`, retention window, cleanup step, app-driven `LSET` retrofit (with the full→short address migration), rotation/retrofit tests.
@@ -0,0 +1,59 @@
# Signed service requests
Optional Ed25519 signature on service RPC requests, constructed and verified in the agent (not the bot), bound to the request's double ratchet. Requests only — responses stay authenticated by the address/ratchet. Signing is optional; a bot decides whether to require it. The agent is stateless: the meaning of a signer key (identity, resource) is the bot's concern.
## Wire — `Simplex.Messaging.Agent.Protocol`
Extend the existing `'A'` inner message; `Maybe` absent = unsigned, so the unsigned path is unchanged:
```haskell
AgentServiceRequest (NonEmpty SMPQueueInfo) (Maybe RequestSignature) MsgBody
data RequestSignature = RequestSignature C.PublicKeyEd25519 (C.Signature 'C.Ed25519)
```
## Binding
```
binding = sha3-256("SimpleXService" <> rcAD)
sig = Ed25519.sign(sk, binding <> payload)
```
The service recomputes `binding` from its own `rcAD` and verifies.
- `rcAD` = the ratchet associated data (`Ratchet.rcAD`) — the shared connection security code: identical on both ratchets by construction (`pubKey(requester ephemeral) <> pubKey(service key)`), stable, and unique per request (fresh requester ephemeral). Already on the ratchet; nothing derived or stored.
- sha3-256 here is not for uniformity or secrecy (both moot: the value is signed, not keyed, and only a ratchet holder can craft a valid request). It gives a canonical fixed-length, domain-tagged binding; the 32-byte fixed prefix also makes `binding <> payload` unambiguous.
- Domain string `"SimpleXService"`: separates this signature from other uses of the signing key.
- Not covered: reply queues (the AEAD protects them in transit; addresses may use redundant queues).
- Anti-relay: a signature bound to one session's rcAD does not verify under another's (both parties' keys differ). Replay of the encrypted blob is handled separately by transport dedup.
## Sign (requester) — `Simplex.Messaging.Agent`
- `sendServiceRequest` / `sendServiceRequestAsync` gain a `Maybe` Ed25519 signing key.
- `joinConnSrv'` DR path takes the ratchet straight from the `createRatchet_`/`getSndRatchet` line (both now yield `(RatchetX448, params)`) and computes `serviceReqBinding` from its `rcAD`; the `mkInner :: SMPQueueInfo -> ByteString -> AgentMessage` closure calls `signServiceReq signKey_ binding payload``RequestSignature pub (sign' pk (binding <> payload))` when a key is given, `Nothing` otherwise.
- Async carries the key in `JRServiceReq {requestKey :: Maybe C.PrivateKeyEd25519}` (enabled `StrEncoding (PrivateKey Ed25519)`); the JOIN worker deserializes it and signs after building the ratchet.
**Status:** implemented and tested in simplexmq-3 (sync + async); invalid signature → `A_SERVICE ASEBadSignature` (logs + `ERR` event), no invitation.
## Verify (service) — `smpContactRequest`
After `initRcvRatchet_` + decrypt, on `AgentServiceRequest (replyQueue :| _) sig_ payload`, one helper does the check:
`verifyServiceReq rc payload sig_ :: Either String (Maybe C.PublicKeyEd25519)`
- `Nothing``Right Nothing` (unsigned).
- `Just (RequestSignature key sig)` → recompute `serviceReqBinding rc` and `C.verify' key sig (binding <> payload)`; `Right (Just key)` if valid, else `Left err`.
Then:
- `Right key_``storeInvitation … True` + `notify $ SREQ invId key_ payload`.
- `Left err``logError` + `notify (ERR (AGENT (A_SERVICE ASEBadSignature)))`, no invitation.
Dedup unchanged.
## Event / API
- `SREQ :: InvitationId -> Maybe C.PublicKeyEd25519 -> MsgBody -> AEvent AEConn`.
- `StrEncoding (PrivateKey Ed25519)` enabled so a caller (e.g. via `JRServiceReq`) can carry the signing key.
## Tests
- `testSignedServiceRequest` (sync) + `testSignedServiceRequestAsync` — signed round-trip delivers the exact signer key on `SREQ` (`sigKey_ == Just signPub`).
- Unsigned path unchanged (existing service tests carry `Nothing` for the new field).
@@ -0,0 +1,81 @@
## Root cause: PRXY errors are attributed to the forwarding server instead of the destination relay
When private routing is enabled and the destination relay is unreachable, the client reports
**"Error connecting to forwarding server smp5.simplex.im"** — naming a preset server that the client
connected to successfully. Retrying rotates to the next proxy (`getNextServer`, `Agent/Client.hs:689`)
and produces the same message with a different preset server, so the destination server is never named
and the failure looks like an outage of our own infrastructure.
### Reproduction
Connecting to a contact address on an unresolvable host (`simplex.server.home`, no DNS record):
```
-- private routing off (correct)
BROKER {brokerAddress = "smp://VvXX…@simplex.server.home:5223",
brokerErr = NETWORK {networkError = NEConnectError {connectError = "…does not exist (Name or service not known)"}}}
-- private routing on (misattributed)
SMP {serverAddress = "smp://…@smp5.simplex.im,…onion",
smpErr = PROXY {proxyErr = BROKER {brokerErr = NETWORK {networkError = NEFailedError}}}}
```
### The asymmetry between the two proxied paths
A server returns `PROXY (BROKER …)` only from `smpProxyError` (`Client.hs:804-815`), which is called
exclusively where the proxy failed to reach the relay — `PRXY` (`Server.hs:1444`) and `PFWD`
(`Server.hs:1466`). The error therefore *always* describes the proxy→relay hop. The two paths then
diverge in how the agent wraps it:
**PFWD — keeps both addresses** (`Agent/Client.hs:1183-1189`): the proxy's error arrives as
`Left ProxyClientError` and is thrown as `PROXY {proxyServer, relayServer, proxyErr}`.
**PRXY — drops the relay** (`Agent/Client.hs:713`): `connectSMPProxiedRelay` has no `Either` layer, so
the error arrives as `PCEProtocolError` and `liftClient SMP` maps it to `SMP <proxyAddr> (PROXY …)`
(`Agent/Client.hs:1244`). The destination address is discarded.
Both clients read the second shape as a client→proxy failure and word it accordingly
(`SimpleXAPI.kt:2692`, `ErrorAlert.swift:117`), which is never what it means.
### Fix
In `newProxiedRelay`, map proxy-reported `PROXY (BROKER …)` errors to the same shape `PFWD` already
produces:
```haskell
proxyRelayError :: HostName -> ErrorType -> AgentErrorType
proxyRelayError proxyHost = \case
e@(SMP.PROXY (SMP.BROKER _)) -> PROXY {proxyServer = protocolClientServer smp, relayServer = …destSrv, proxyErr = ProxyProtocolError e}
e -> SMP proxyHost e
```
`liftClient` applies this only to `PCEProtocolError`, so genuine client↔proxy failures (response
timeout, network error, proxy transport version) still map to `BROKER <proxy> …` and remain attributed
to the proxy. Both apps already render the resulting shape correctly, with no client change:
*"Forwarding server smp5.simplex.im failed to connect to destination server simplex.server.home."*
The guard is `BROKER` rather than every `ProxyError`, so the remap covers exactly the misattributed
class and nothing else. `BASIC_AUTH` is deliberately excluded — the proxy returns it when proxying is
disabled or the basic auth does not match (`Server.hs:1416-1420`), which is a client↔proxy fact and is
correctly attributed today. `NO_SESSION` is returned only for `PFWD`. `PROTOCOL` describes the relay
but is not rendered as a proxy-connection error by either client, so leaving it unchanged keeps the
diff to the errors that actually produce a wrong message.
### Blast radius
- `temporaryAgentError` (`Agent/Client.hs:1572-1580`) and `serverHostError` (`:1594-1596`) already match
both shapes with the same helpers — retry and proxy-fallback behaviour is unchanged.
- `clientServiceError` (`:1268-1273`) has no `PROXY`-shape twin for `BROKER NO_SERVICE`, but both ends
document that case as unreachable (`Client.hs:812`); left as is.
- simplex-chat `Subscriber.hs:1819-1820` handles both shapes; send failures move from `SndErrProxy` to
`SndErrProxyRelay`, i.e. "Destination server error" rather than "Error" — also more accurate.
- `SMP _ (PROXY _)` becomes unreachable, making `smpProxyErrorAlert` in both clients dead code. Removing
it is a follow-up in simplex-chat, not required by this change.
### Verification
- Reproduced before/after with a CLI built against this branch: the error now carries
`relayServer = "smp://VvXX…@simplex.server.home:5223"`, and the direct (non-proxied) path is
byte-identical to before.
- `SMPProxyTests`: 45 examples, 0 failures — including `fails when fallback is prohibited` and both
retry tests, which exercise `newProxiedRelay` and the error classification.
@@ -0,0 +1,187 @@
# Fast queue rotation — implementation plan
Branch: ep/drop-agent-versions. RFC: ../rfcs/2026-08-09-fast-queue-rotation.md.
Model: redundant delivery, no flip. `QADD` adds the new receive queue R'. From `QADD` until R' is
secured the sender writes every message to both old and R' (double delivery, not a move); once R' is
secured the sender writes new messages to R' only, while old delivers its already-scheduled tail and
the `QEND` appended to it. `QEND` removes a named queue. The recipient drops duplicates (double
ratchet), so the order and which queue delivers do not matter, as long as every message arrives on at
least one queue. Rotation away from a dead server works because every message up to securing is
scheduled on R' too.
Roles: A initiates (its receive queue rotates; A receives on R'). B sends to A and secures R'.
## Why redundant delivery removes the hard parts
- No boundary, no drain, no last-message id. A never decides how much of old to read.
- A dead old server loses nothing B still holds: every undelivered message is scheduled on R' as well.
- A dead new server does not suspend delivery: old keeps delivering until R' is secured.
- The double ratchet already drops duplicates (`AGENT A_DUPLICATE`) and tolerates bounded reordering,
and the delivery schema already writes one message to several send queues (`enqueueMessageB` +
`enqueueSavedMessageB`).
## The one ordering constraint
A must hold R''s secret before it reads any R' data message. A data message reaching R' before the
confirmation is dropped as "no keys" (`processClientMsg`, `(Nothing, Nothing)` arm, line 3611), which
loses it when old is dead. So the confirmation is the first message B sends on R'. R''s delivery
worker does not start while R' is securing, so its accumulated rows cannot outrun the confirmation.
`ICQSndSecure` sends the confirmation and only then starts the worker. This holds on restart too (see
Worker gate).
## New definitions
Agent/Protocol.hs
- Condition fast rotation on the existing `rpcAddressSMPAgentVersion` (v8, `Protocol.hs:322`).
- New `AMessage` constructor `QEND SndQAddr` (tag `QE`), the address of the queue to remove. v8-only,
and only sent during fast rotation, so peers below v8 never parse it.
- `SndSwitchStatus` constructors `SSSecuringQueue` (old, while R' secures) and `SSSendingQEND` (old,
after R' is secured — it drains its tail and `QEND` but takes no new messages).
- `InternalCommand` constructor `ICQSndSecure SMP.SenderId`.
No receive-side switch status, no boundary, no drain state.
## Schema
None. `SSSecuringQueue` uses `snd_queues.switch_status`; R' is secured into `rcv_queues.e2e_dh_secret`.
No new columns, no migration.
## Sender B
### Dual scheduling from QADD
`enqueueMessageB` writes a delivery row for the head send queue and for each `filter isActiveSndQ`
tail queue (`Agent.hs:2345`). Adjust the selection two ways: additionally include a securing
replacement queue on a v8 connection (`connAgentVersion cData >= rpcAddressSMPAgentVersion && status == New && isJust dbReplaceQueueId`),
and exclude a terminating queue (`sndSwchStatus == Just SSSendingQEND`). The version guard keeps the
slow path unchanged — there R' is also `New` with a replace reference during `QKEY`/`QUSE`, but it must
not be dual-scheduled. `SSSendingQEND` is a fast-path-only status, so the exclusion never affects the
slow path. The gate below is inert for the slow path anyway, since it never starts R''s worker while
`New`.
- `QADD` until R' secured: old is the head (active) and R' is the securing replacement, so every `SEND`
writes both rows. old delivers at once; R''s rows accumulate behind its gate.
- R' secured: R' is the head (primary) and old is `SSSendingQEND` (excluded), so a `SEND` writes R'
only. old keeps its worker and delivers whatever was already scheduled on it, plus `QEND`.
### Worker gate
`submitPendingMsg` (`Agent.hs:2437`) and `resumeMsgDelivery` (`2421`) — the two `getDeliveryWorker`
callers that start delivery — skip a queue with `status == New && isJust dbReplaceQueueId`, so neither
a `SEND` nor startup starts R''s worker while it secures. Startup resumes delivery through
`resumeMsgDelivery` (`resumeDelivery` line 1848, and `getAllSndQueuesForDelivery` line 1943), so R' is
skipped there; `resumeAllCommands` (1883) resumes R''s `ICQSndSecure`, which secures R' and only then
starts its worker.
### Steps
`qAddMsg` (fast branch, under the connection lock, `Agent.hs:3855`):
- Add R' as the slow path does (line 3870): `addConnSndQueue (sq_) {primary = True, dbReplaceQueueId = Just old}`, `New`.
- Duplicate **every** undelivered message on old to R': for each pending row on old
(`SELECT internal_id FROM snd_message_deliveries WHERE conn_id = ? AND snd_queue_id = old AND failed = 0`),
`createSndMsgDelivery db R' internalId`. This is the loss-prevention step: old's not-yet-sent
messages are duplicated onto R', so if old later fails they are already on R'. If old is already
down, nothing was sent and the whole backlog is duplicated.
- `enqueueCommand (Just newSrv) (ICQSndSecure sndId)`; `setSndSwitchStatus SSSecuringQueue` on old
(where the slow path sets `SSSendingQKEY`, line 3874); notify `SWITCH QDSnd SPStarted`. old keeps
delivering; R''s worker is gated.
`ICQSndSecure sId` (retryable, under `tryWithLock`):
1. If old is already gone, a prior attempt finished; return. Otherwise find R' by `sId`.
2. `secureSndQueue` (SKEY) R' — idempotent, since `sndPrivateKey` was persisted by `qAddMsg`
(`QueueStore/STM.hs:213`: same key → `Right ()`, different key → `AUTH`).
3. Send the confirmation on R' (`sendConfirmation`; empty body, both peers already know each other;
`e2eEncryption_ = Nothing`, no ratchet step). It is the first message on R'.
4. On success, in one transaction: `setSndQueueStatus R' Active` (the gate lifts), `setSndQueuePrimary R'`
(R' becomes the head; its replace reference is cleared), and `setSndSwitchStatus old (Just SSSendingQEND)`
(old takes no new messages but keeps its worker). Then `submitPendingMsg c R'` (the worker starts and
flushes the accumulated rows after the confirmation), `enqueueMessages [old, R'] (QEND oldAddr)`
(appended after old's tail, delivered on both), and notify `SWITCH QDSnd SPSecured`. From here a
`SEND` goes to R' only; old delivers its tail then `QEND` and is removed when `QEND` is sent.
A temporary error retries from step 1; nothing is torn down. A permanent `AUTH` (should not occur, R'
was secured with B's own key) leaves both queues and surfaces `A_QUEUE`.
`QEND` sent — new `AM_QEND_` arm in `runSmpQueueMsgDelivery`, modelled on `AM_QTEST_` (`Agent.hs:2567`):
on a successful send of `QEND addr`, remove the named send queue (`TM.delete` its worker,
`deleteConnSndQueue addr`), make the remaining queue the sole primary (`setSndQueuePrimary`, which
clears its `replace_snd_queue_id`), and notify `SWITCH QDSnd SPCompleted` (as `AM_QTEST_` does, line
2591). This re-primary is a no-op on the send side — step 4 already made R' primary before `QEND` was
enqueued. The handler is idempotent — a second `QEND` send finds the named queue already gone and does
nothing. `QEND` is sent on both queues; removing old's send queue also drops any `QEND` still pending
on old. The R' copy reliably removes old
and reaches A even when old is dead; the old copy is best effort. Once old's send queue is gone, `SEND`
schedules to R' only.
## Recipient A
A is subscribed to old (primary, `RSSendingQADD`) and R' (created at rotation start,
`dbReplaceQueueId = old`).
- **Confirmation on R'.** In `processClientMsg`, `(Nothing, Just e2ePubKey)` case, add an arm before the
`senderCanSecure` arm (`Agent.hs:3476`), guarded by `isJust (dbReplaceQueueId rq)`. In one
transaction: `setRcvQueueConfirmedE2E rq (C.dh' e2ePubKey e2ePrivKey) (min v phVer)` (secures R') and
`setRcvQueuePrimary R'` (clears R''s replace reference). Then `ack`, and notify `SWITCH QDRcv SPConfirmed`.
No conn-info processing, no ratchet step, no deferral. Redelivery is idempotent: R' now has `e2e_dh_secret`, so a re-sent
confirmation reaches the `(Just e2eDh, Just _)` arm (line 3608) and is acked — correct here, since
there is no backlog to hold.
- **Data on R'.** With R''s replace reference cleared, a data message on R' takes the ordinary path
(`(_, dbReplaceQueueId=Nothing)`, line 3503) — no old-deletion, no `RSSendingQUSE` check. A copy
already read on old is dropped as `A_DUPLICATE`; a copy read first on R' advances the ratchet and
old's copy is then the duplicate.
- **`QEND oldAddr` on either queue.** New `AMessage` handler (`qEndMsg`, a `qDuplex` handler like
`qAddMsg`): `findRQ oldAddr` the receive queue to remove. Mark it deleted (`setRcvQueueDeleted`, so
`getRcvQueuesByConnId_`'s `deleted = 0` filter excludes it at once and a restart does not resurrect
it) and `enqueueCommand (Just oldServer) (ICDeleteRcvQueue oldRcvId)` for the server `DEL` and record
removal — the async, crash-safe path `abortConnectionSwitch` uses, which resumes on restart and does
not block `QEND`, **not** the synchronous `deleteQueue` of `finalizeSwitch`, which would stall if old
is unreachable. `ICDeleteRcvQueue` (`Agent.hs:2224`) currently retries a temporary error forever;
bound it with the same persisted `rcv_queues.delete_errors`/`deleteErrorCount` mechanism `deleteQueueRec`
uses (2884): on a temporary error `incRcvDeleteErrors`, and at the limit `deleteConnRcvQueue` and
stop. The count is in the database, so the bound survives restarts, and its only other caller
(`abortConnectionSwitch'`, 2739) deletes an alive queue that succeeds well before the limit. `qEndMsg`
does **not** re-primary R' — the confirmation arm (above) owns R''s primary flag and replace
reference. `QEND` on old and the confirmation on R' travel on different queues with no order between
them, so `QEND` on old can be processed first (it sits only behind old's tail); re-primarying then
would clear R''s `dbReplaceQueueId` and the later confirmation would miss the rotation arm and never
secure R'. So `qEndMsg` only removes the named queue. Re-create the notification subscription
(`when enableNtfs $ sendNtfSubCommand ns (NSCCreate, [connId])`); notify
`SWITCH QDRcv SPCompleted`; `ackDel` the `QEND`. Received on both queues, the second finds it already
marked deleted and is a no-op.
No drain, no boundary, no finalize command. Old is removed when `QEND` arrives, not by counting.
## Abort / version
- Fast rotation runs only when `connAgentVersion >= v8`; otherwise the `QKEY`/`QUSE` slow path runs
unchanged.
- `canAbortRcvSwitch` (`Agent/Store.hs:210`) returns false for `RSSendingQADD` when `connAgentVersion >= v8`
(at v8 B always chooses fast, so A treats a sent `QADD` as committed). Its signature gains
`connAgentVersion`; both callers pass it from `cData``abortConnectionSwitch'` (2730) and
`rcvQueueInfo` in `connectionStats` (2976).
## Losses and duplication
- No boundary loss: the `QADD` step **duplicates** old's entire undelivered backlog onto R', and every
later message up to securing is scheduled on both queues, so if old fails it loses nothing B still
holds. The only messages old can strand are those its server already accepted but had not handed to
A — the ordinary store-and-forward risk, present whenever a server fails with unread messages, and
empty if old was already down (a down server accepted nothing).
- Duplicates: the double ratchet drops them (`A_DUPLICATE`); `checkMsgIntegrity`'s `MsgDuplicate` is
only a flag, not the mechanism.
- The 512 skip bound (`Crypto/Ratchet.hs:953`) does not bite on the rotation: each queue delivers in
order and every message B still holds is on R', so A reads a contiguous stream with only small
cross-queue reordering. A store-and-forward residual (above) is an ordinary loss, not introduced here.
## Tests
- new/new, old stopped right after `QADD`: rotation completes; all messages delivered on R'; old
removed by `QEND` on R'.
- new/new, both alive: messages delivered on both, deduped; old removed by `QEND`.
- new/old and old/new: fall back to the `QKEY`/`QUSE` path.
- crash during securing: restart does not start R''s worker; `ICQSndSecure` resumes, secures R',
starts the worker, sends `QEND`.
- `QEND` received on both queues: old removed once, the second receipt is a no-op.
- `QEND` on old processed before the confirmation on R': R' still secures, because `qEndMsg` does not
clear R''s replace reference; rotation completes.
@@ -0,0 +1,152 @@
# Server: batched SUB command processing
Implementation plan for Part 1 of [RFC 2026-03-28-subscription-performance](../rfcs/2026-03-28-subscription-performance.md).
## Current state
When a batch of ~135 SUB commands arrives, the server already batches:
- Queue record lookups (`getQueueRecs` in `receive`, Server.hs:1151)
- Command verification (`verifyLoadedQueue`, Server.hs:1152)
But command processing is per-command (`foldrM process` in `client`, Server.hs:1372-1375). Each SUB calls `subscribeQueueAndDeliver` which calls `tryPeekMsg` - one DB query per queue. For Postgres, that's ~135 individual `SELECT ... FROM messages WHERE recipient_id = ? ORDER BY message_id ASC LIMIT 1` queries per batch.
## Goal
Replace ~135 individual message peek queries with 1 batched query per batch. No protocol changes.
## Implementation
### Step 1: Add `tryPeekMsgs` to MsgStoreClass
File: `src/Simplex/Messaging/Server/MsgStore/Types.hs`
Add to `MsgStoreClass`:
```haskell
tryPeekMsgs :: s -> [StoreQueue s] -> ExceptT ErrorType IO (Map RecipientId Message)
```
Returns a map from recipient ID to earliest pending message for each queue that has one. Queues with no messages are absent from the map.
### Step 2: Parameterize `deliver` to accept pre-fetched message
File: `src/Simplex/Messaging/Server.hs`
Currently `deliver` (inside `subscribeQueueAndDeliver`, line 1641) calls `tryPeekMsg ms q`. Add a parameter for an optional pre-fetched message:
```haskell
deliver :: Maybe Message -> (Bool, Maybe Sub) -> M s ResponseAndMessage
deliver prefetchedMsg (hasSub, sub_) = do
stats <- asks serverStats
fmap (either ((,Nothing) . err) id) $ liftIO $ runExceptT $ do
msg_ <- maybe (tryPeekMsg ms q) (pure . Just) prefetchedMsg
...
```
When `Nothing` is passed, falls back to individual `tryPeekMsg` (existing behavior). When `Just msg` is passed, uses it directly (batched path).
### Step 3: Pre-fetch messages before the processing loop
File: `src/Simplex/Messaging/Server.hs`
Currently (lines 1372-1375):
```haskell
forever $
atomically (readTBQueue rcvQ)
>>= foldrM process ([], [])
>>= \(rs_, msgs) -> ...
```
Add a pre-fetch step before the existing loop:
```haskell
forever $ do
batch <- atomically (readTBQueue rcvQ)
msgMap <- prefetchMsgs batch
foldrM (process msgMap) ([], []) batch
>>= \(rs_, msgs) -> ...
```
`prefetchMsgs` scans the batch, collects queues from SUB commands that have a verified queue (`q_ = Just (q, _)`), calls `tryPeekMsgs` once, returns the map. For batches with no SUBs it returns an empty map (no DB call).
`process` passes the looked-up message (or Nothing) through to `processCommand` and down to `deliver`.
The `foldrM process` loop, `processCommand`, `subscribeQueueAndDeliver`, and all other command handlers stay structurally the same. Only `deliver` gains one parameter, and the `client` loop gains one pre-fetch call.
### Step 4: Review
Review the typeclass signature and server usage. Confirm the interface has the right shape before implementing store backends.
### Step 5: Implement for each store backend
#### Postgres
File: `src/Simplex/Messaging/Server/MsgStore/Postgres.hs`
Single query using `DISTINCT ON`:
```sql
SELECT DISTINCT ON (recipient_id)
recipient_id, msg_id, msg_ts, msg_quota, msg_ntf_flag, msg_body
FROM messages
WHERE recipient_id IN ?
ORDER BY recipient_id, message_id ASC
```
Build `Map RecipientId Message` from results.
#### STM
File: `src/Simplex/Messaging/Server/MsgStore/STM.hs`
Loop over queues, call `tryPeekMsg` for each, collect into map.
#### Journal
File: `src/Simplex/Messaging/Server/MsgStore/Journal.hs`
Loop over queues, call `tryPeekMsg` for each, collect into map.
### Step 6: Handle edge cases
1. **Mixed batches**: `prefetchMsgs` collects only SUB queues. Non-SUB commands get Nothing for the pre-fetched message and process unchanged.
2. **Already-subscribed queues**: Include in pre-fetch - `deliver` is called for re-SUBs too (delivers pending message).
3. **Service subscriptions**: The pre-fetch doesn't care about service state. `sharedSubscribeQueue` handles service association in STM; message peek is the same.
4. **Error queues**: Verification errors from `receive` are Left values in the batch. `prefetchMsgs` only looks at Right values with SUB commands.
5. **Empty pre-fetch**: If batch has no SUBs (e.g., all ACKs), `prefetchMsgs` returns empty map, no DB call made.
### Step 7: Batch other commands (future, not in scope)
The same pattern (pre-fetch before loop, parameterize handler) can extend to:
- `ACK` with `tryDelPeekMsg` - batch delete+peek
- `GET` with `tryPeekMsg` - same map lookup
Lower priority since these don't have the N-at-once pattern of subscriptions.
## File changes summary
| File | Change |
|---|---|
| `src/Simplex/Messaging/Server/MsgStore/Types.hs` | Add `tryPeekMsgs` to typeclass |
| `src/Simplex/Messaging/Server/MsgStore/Postgres.hs` | Implement `tryPeekMsgs` with batch SQL |
| `src/Simplex/Messaging/Server/MsgStore/STM.hs` | Implement `tryPeekMsgs` as loop |
| `src/Simplex/Messaging/Server/MsgStore/Journal.hs` | Implement `tryPeekMsgs` as loop |
| `src/Simplex/Messaging/Server.hs` | Add `prefetchMsgs`, parameterize `deliver` |
## Testing
1. Existing server tests must pass unchanged (correctness preserved).
2. Add a test that subscribes a batch of queues (some with pending messages, some without) and verifies all get correct SOK + MSG responses.
3. Prometheus metrics: existing `qSub` stat should still increment correctly.
## Performance expectation
For 300K queues across ~2200 batches:
- Before: ~300K individual DB queries
- After: ~2200 batched DB queries (one per batch of ~135)
- ~136x reduction in DB round-trips
@@ -0,0 +1,126 @@
# Server: batch queue service associations
When a batch of SUB or NSUB commands arrives from a service client, each command that needs a new or removed service association calls `setQueueService` individually - one DB write per command. For 135 commands per batch, that's 135 individual `UPDATE msg_queues` queries.
## Goal
Reduce to at most 2 DB queries per batch (one for rcv associations, one for ntf associations), using `UPDATE ... RETURNING recipient_id` to identify which queues were actually updated.
Also fuse message pre-fetch and association batching into a single batch preparation step with a clean contract.
## Contract
```haskell
prepareBatch :: Maybe ServiceId -> NonEmpty (VerifiedTransmission s) -> M s (Either ErrorType (Map RecipientId (Maybe Message, Maybe (Either ErrorType ()))))
```
`Left e` = batch-level failure (message pre-fetch or association query failed entirely). All SUBs/NSUBs in the batch get this error.
`Right map` = per-queue results as a tuple:
- `Maybe Message` - pre-fetched message for SUB queues, `Nothing` for NSUB or no message
- `Maybe (Either ErrorType ())` - association result. `Nothing` = no update needed. `Just (Right ())` = update succeeded. `Just (Left e)` = update failed for this queue.
One map, one lookup per queue. `processCommand` passes both values to `subscribeQueueAndDeliver` / `subscribeNotifications` -> `sharedSubscribeQueue`.
Queues not in the map (non-SUB/NSUB commands, failed verification) are not affected.
## prepareBatch implementation
One accumulating fold over the batch, collecting three lists:
- `subMsgQs :: [StoreQueue s]` - SUB queues for message pre-fetch
- `rcvAssocQs :: [StoreQueue s]` - SUB queues needing `rcv_service_id` update (`clntServiceId /= rcvServiceId qr`)
- `ntfAssocQs :: [StoreQueue s]` - NSUB queues needing `ntf_service_id` update (`clntServiceId /= ntfServiceId` from `NtfCreds`)
Classification reads from the already-loaded `QueueRec` in `VerifiedTransmission` - no extra DB query.
Then three store calls (each skipped if its list is empty):
1. `tryPeekMsgs ms subMsgQs` -> `Map RecipientId Message`
2. `setRcvQueueServices (queueStore ms) clntServiceId rcvAssocQs` -> `Set RecipientId`
3. `setNtfQueueServices (queueStore ms) clntServiceId ntfAssocQs` -> `Set RecipientId`
Then one pass to merge results into `Map RecipientId (Maybe Message, Maybe (Either ErrorType ()))`:
- For each SUB queue: `(M.lookup rId msgMap, assocResult rId rcvUpdated rcvAssocQs)`
- For each NSUB queue: `(Nothing, assocResult rId ntfUpdated ntfAssocQs)`
Where `assocResult rId updated assocQs` = if the queue was in `assocQs` (needed update), then `Just (Right ())` if `rId` is in `updated`, else `Just (Left AUTH)`. If not in `assocQs` (no update needed), `Nothing`.
If any of the three calls fails entirely, return `Left e`.
## Store interface
Replace the polymorphic `setQueueServices` with two plain functions in `QueueStoreClass`:
```haskell
setRcvQueueServices :: s -> Maybe ServiceId -> [q] -> IO (Set RecipientId)
setNtfQueueServices :: s -> Maybe ServiceId -> [q] -> IO (Set RecipientId)
```
No `SParty p` polymorphism. Each function knows its column.
### Postgres implementation
`setRcvQueueServices`:
```sql
UPDATE msg_queues SET rcv_service_id = ?
WHERE recipient_id IN ? AND deleted_at IS NULL
RETURNING recipient_id
```
`setNtfQueueServices`:
```sql
UPDATE msg_queues SET ntf_service_id = ?
WHERE recipient_id IN ? AND notifier_id IS NOT NULL AND deleted_at IS NULL
RETURNING recipient_id
```
After each batch query, for each queue in the returned set:
1. Read QueueRec TVar, update with new serviceId
2. Write store log entry
### STM implementation
Loop over queues, call existing per-item logic, collect succeeded `RecipientId`s into a Set.
## Downstream changes in Server.hs
### processCommand
Gains one parameter: `Map RecipientId (Maybe Message, Maybe (Either ErrorType ()))`.
SUB case: `M.lookup entId prepared` gives `Just (msg_, assocResult)` or `Nothing`. Pass both to `subscribeQueueAndDeliver`.
NSUB case: `M.lookup entId prepared` gives `Just (Nothing, assocResult)` or `Nothing`. Pass `assocResult` to `subscribeNotifications`.
Forwarded commands: pass `M.empty`.
### subscribeQueueAndDeliver
Takes `Maybe Message` and `Maybe (Either ErrorType ())` as before. No change in how it uses them.
### sharedSubscribeQueue
Takes `Maybe (Either ErrorType ())`. On paths needing association update:
- `Just (Left e)` -> return error
- `Just (Right ())` -> skip `setQueueService`, proceed with STM work
- `Nothing` -> no update needed, proceed with existing logic
## Implementation order (top-down)
1. Define the `prepareBatch` contract and thread one map through `processCommand` -> `subscribeQueueAndDeliver` / `subscribeNotifications` -> `sharedSubscribeQueue` (Server.hs)
2. Implement `prepareBatch` with the fold, three calls, and merge (Server.hs)
3. Add `setRcvQueueServices` and `setNtfQueueServices` to `QueueStoreClass` (Types.hs)
4. Implement for Postgres with batch `UPDATE ... RETURNING` (Postgres.hs)
5. Implement for STM as loop (STM.hs)
6. Implement for Journal as delegation (Journal.hs)
At step 2, store functions can initially be stubs returning empty sets. Steps 3-6 fill in the real implementations.
## Files changed
| File | Change |
|---|---|
| `src/Simplex/Messaging/Server.hs` | `prepareBatch` with fold + merge; one map parameter through `processCommand` -> `subscribeQueueAndDeliver` / `subscribeNotifications` -> `sharedSubscribeQueue` |
| `src/Simplex/Messaging/Server/QueueStore/Types.hs` | Add `setRcvQueueServices`, `setNtfQueueServices` to `QueueStoreClass` |
| `src/Simplex/Messaging/Server/QueueStore/Postgres.hs` | Implement with batch `UPDATE ... RETURNING` + per-item TVar/log updates |
| `src/Simplex/Messaging/Server/QueueStore/STM.hs` | Implement as loop |
| `src/Simplex/Messaging/Server/MsgStore/Journal.hs` | Delegate to underlying store |
+455
View File
@@ -0,0 +1,455 @@
# Server: SMP support for public namespaces
> **⚠ Implementation diverged from this plan.** Six audit rounds reshaped the
> original design. **The shipped code differs in several load-bearing ways:**
>
> - **Wire format**: `NameRecord` is now JSON (aeson), not the custom binary
> ABNF this plan documents. See `protocol/simplex-messaging.md` §Resolver
> commands and `src/Simplex/Messaging/Protocol.hs` ToJSON/FromJSON instances.
> - **No cache**: the TTL + FIFO + byte-cap cache, in-flight coalescing,
> `psqueues` dep, and `cache_*` INI keys are all gone. Every RSLV becomes
> one `eth_call` bounded by `rpcMaxConcurrency` + `rpcTimeoutMs`. See
> `src/Simplex/Messaging/Server/Names.hs`.
> - **No `allow_dangerous_colocation` flag**: the proxy co-location guard
> was demoted to a startup `logWarn` (the flag was always-on because
> `[PROXY]` has no enable toggle).
> - **Module shape**: `Names/Resolver.hs` was merged into `Names.hs`; only
> `Names/Eth/RPC.hs` and `Names/Eth/SNRC.hs` remain as separate modules.
> - **Test list**: of the 15 specs listed below, ~7 shipped; the rest were
> either superseded by the cache removal (CacheSpec) or deferred
> (ForwardedRslvSpec, MockRpcSpec, StartupGuardSpec, UrlValidationSpec,
> EipChecksumSpec).
>
> Sources of truth: `CHANGELOG.md` (release notes),
> `protocol/simplex-messaging.md` §Resolver commands (wire format),
> `src/Simplex/Messaging/Server/Names*.hs` (implementation). This file is
> retained as historical context; do not treat it as a specification.
Implementation plan for Part 2 of [RFC 2026-05-21-public-namespaces](https://github.com/simplex-chat/simplex-chat/blob/ep/namespace/docs/rfcs/2026-05-21-public-namespaces.md). Adds a forwarded-only `RSLV <lookup_key>` SMP command that returns `NAME <NameRecord>` read from the SNRC contract via a Reth+Nimbus JSON-RPC endpoint. Smp-server becomes name-capable by `[NAMES] enable: on`.
Out of scope: `Simplex.Messaging.Client` API, agent-side resolution flow, `ServerRoles.names` in the agent, default-router list, reverse resolution, multicoin/text records, state proofs.
## Architecture
```mermaid
sequenceDiagram
participant C as Client
participant P as Proxy (storage role)
participant N as Name server (names role)
participant E as Ethereum endpoint<br/>(Reth+Nimbus)
C ->> P: PFWD(enc(RSLV key))
P ->> N: RFWD(enc(RSLV key))
note over N: verifyTransmission True →<br/>vc SResolver (RSLV _) → VRVerified
N ->> N: cache lookup
alt cache miss
N ->> E: eth_call(SNRC, namehash(key))
E -->> N: ABI bytes
note over N: ABI decode + zero-owner check + cache insert
end
N -->> P: RFWD(enc(NAME rec | ERR AUTH))
P -->> C: PRES(enc(NAME rec | ERR AUTH))
```
RSLV is **forwarded-only** — direct RSLV is rejected `CMD PROHIBITED`. This preserves the RFC's two-server resolution: the name server sees the lookup key but never the client's IP, session, or identity.
## Protocol
Shared library: `src/Simplex/Messaging/Protocol.hs` and `src/Simplex/Messaging/Transport.hs`.
**Version.** `Transport.hs:226`: `namesSMPVersion = VersionSMP 20`. Bump `currentClientSMPRelayVersion`, `currentServerSMPRelayVersion`, `proxiedSMPRelayVersion` to 20. Pre-v20 binaries lack the `RSLV_` tag; v20 binaries with sessions negotiated at v < 20 reject `RSLV_` at the parameter parser. The proxied-version bump 18 → 20 is safe (v19's `RecipientService`/`NotifierService` aren't in the forwarded whitelist; v18's `BLOCKED info` is already version-branched at `Protocol.hs:1943`).
**Party kind.** Append `Resolver` to `Party` (line 335); add `SResolver` (line 349), `TestEquality` clause (line 361), `PartyI Resolver` (line 394). `queueParty SResolver = Nothing` (falls through line 412). `partyClientRole SResolver = Nothing`.
**`RSLV` command.**
```haskell
RSLV :: LookupKey -> Command Resolver
newtype LookupKey = LookupKey ByteString
instance Encoding LookupKey where
smpEncode (LookupKey s) = smpEncode s
smpP = do
n <- lenP
when (n > 64) $ fail "LookupKey too large"
LookupKey <$> A.take n
```
Name-syntax validation is client-side per RFC; the server treats the key as opaque bytes. Tag `"RSLV"`, version guard inside `protocolP v (CT SResolver RSLV_)`: `| v >= namesSMPVersion -> Cmd SResolver . RSLV <$> _smpP`.
**Testnet/mainnet selector**: how the `#testnet:name` namespace appears in `LookupKey` bytes is determined by the SNRC contract (Part 1) — confirm with Part 1 before merging.
**`NAME` response.**
```haskell
NAME :: NameRecord -> BrokerMsg
```
Tag `"NAME"`. Symmetric version guards on encode (in `encodeProtocol v`) and decode (in `protocolP v NAME_`): `| v >= namesSMPVersion -> ...`. `NameRecord` has **no `Encoding` typeclass instance** — the typeclass cannot version-branch. Use top-level helpers `nameRecBytes :: VersionSMP -> NameRecord -> ByteString` and `parseNameRec :: VersionSMP -> Parser NameRecord`, mirroring the `IDS QIK` precedent at `Protocol.hs:19121979`.
**`NameRecord` schema and wire layout.**
```haskell
data NameRecord = NameRecord
{ nrDisplayName :: Text -- ≤255 bytes UTF-8
, nrOwner :: NameOwner -- 20 raw bytes
, nrChannelLinks :: [NameLink]
, nrContactLinks :: [NameLink]
, nrAdminAddress :: Maybe Text
, nrAdminEmail :: Maybe Text
, nrExpiry :: Int64 -- Unix seconds, ≥ 0
, nrIsTest :: Bool
}
newtype NameOwner = NameOwner ByteString -- bare ctor NOT exported; smart ctor enforces length 20
newtype NameLink = NameLink Text -- bare ctor NOT exported; smart ctor enforces ≤1024 bytes
unNameOwner :: NameOwner -> ByteString
unNameOwner (NameOwner bs) = bs
unNameLink :: NameLink -> Text
unNameLink (NameLink t) = t
```
Field additions are gated by future SMP version bumps (matching the `IDS QIK` precedent at `Protocol.hs:19121979`) — no separate record-version field.
| Field | Encoding | Max bytes |
|---|---|---|
| `nrDisplayName` | 1-byte length prefix + UTF-8 | 1 + 255 |
| `nrOwner` | 20 raw bytes, no prefix | 20 |
| `nrChannelLinks`, `nrContactLinks` | 1-byte count + per-element (Word16 BE len + UTF-8); combined cap **8 entries** across both lists | 1 + Σ(2 + ≤1024) |
| `nrAdminAddress`, `nrAdminEmail` | `'0'` or `'1'` + (1-byte length + UTF-8 if `'1'`) | 1 + 1 + 255 |
| `nrExpiry` | two big-endian `Word32` | 8 |
| `nrIsTest` | `'T'` or `'F'` | 1 |
`Encoding NameLink` reads the Word16 length **before** `A.take` allocates — going through the existing `Large` wrapper allows up to 65 535 bytes per element. There is no `Encoding [a]` instance — use `smpEncodeList` / `smpListP` / a bounded variant:
```haskell
smpListPUpTo :: Encoding a => Int -> Parser [a]
smpListPUpTo cap = do
n <- lenP
when (n > cap) $ fail "list too long"
A.count n smpP
parseNameRec _v = do
nrDisplayName <- smpP
nrOwner <- smpP
nrChannelLinks <- smpListPUpTo 8
nrContactLinks <- smpListPUpTo (8 - length nrChannelLinks)
nrAdminAddress <- smpP
nrAdminEmail <- smpP
nrExpiry <- smpP
when (nrExpiry < 0) $ fail "expiry must be non-negative"
nrIsTest <- smpP
pure NameRecord{..}
```
Both list parsers fail at the count step before allocating; the second inherits the residual budget. Canonical encoding by construction: every primitive has exactly one valid byte form — two name servers reading the same SNRC state produce byte-identical responses.
**Wire-size budget.** `paddedProxiedTLength = 16226` is the plaintext input to `cbEncrypt` (`Server.hs:2117`); `pad` reserves 2 bytes → framed transmission ≤ 16 224 bytes. Combined-link cap 8 yields max payload ≈ 9 050 bytes — generous margin.
**Error semantics.** A single wire code: `ERR AUTH`. Per RFC, this collapses every failure (name not found, malformed key, names disabled, RPC unreachable, decode error, timeout). Resolver internally distinguishes the cause for stats only.
**Forwarded-only access.** Direct RSLV is rejected with `CMD PROHIBITED`. The shape of `THAuthServer` alone cannot discriminate direct from forwarded (`Transport.hs:852` sets `sessSecret' = Just _` for every v6+ direct client too). An explicit `forwarded :: Bool` flag is threaded through `verifyTransmission` (see below).
## Server changes
All edits in `src/Simplex/Messaging/Server.hs`.
**`forwarded :: Bool` plumbing.** Three signatures change:
- `verifyTransmission :: Bool -> ...` (line 1233) — direct path passes `False` (lines 11521153), forwarded path passes `True` (line 2129).
- `verifyLoadedQueue :: Bool -> ...` (line 1238) — receives the flag from `verifyTransmission` (lines 1235, 1240).
- `verifyQueueTransmission :: Bool -> ...` (line 1244) — receives and uses the flag.
New `vc` clauses inside `verifyQueueTransmission`:
```haskell
vc SResolver (RSLV _) | forwarded = VRVerified Nothing
| otherwise = VRFailed (CMD PROHIBITED)
vc SResolver _ = VRFailed (CMD PROHIBITED) -- defensive catch-all
```
**Forwarded whitelist** (`Server.hs:2132`):
```haskell
Cmd SResolver (RSLV _) -> True
```
**`processCommand` branch** (alongside line 1481):
```haskell
Cmd SResolver (RSLV (LookupKey key)) -> do
st <- asks (rslvStats . serverStats)
incStat (rslvReqs st)
asks namesEnv >>= \case
Nothing -> incStat (rslvDisabled st) $> response (corrId, NoEntity, ERR AUTH)
Just nenv -> liftIO (resolveName nenv key) >>= \case
Right rec -> incStat (rslvSucc st) $> response (corrId, NoEntity, NAME rec)
Left NotFound -> incStat (rslvNotFound st) $> response (corrId, NoEntity, ERR AUTH)
Left _ -> incStat (rslvEthErrs st) $> response (corrId, NoEntity, ERR AUTH)
```
**Shutdown.** Add `closeNamesEnv :: NamesEnv -> IO ()` calling `closeManager`. Wire into `closeServer` (`Server.hs:247`):
```haskell
closeServer = do
asks (smpAgent . proxyAgent) >>= liftIO . closeSMPClientAgent
asks namesEnv >>= liftIO . mapM_ closeNamesEnv
```
In-flight `resolveName` calls during shutdown receive `ConnectionClosed``EthHttpErr` → masked-leader cleanup runs → waiters unblock with `ERR AUTH`.
**`incStat` relocation.** Defined at `Server.hs:2220`, currently unexported. Move to `Server/Stats.hs` (one-line transplant + export) so `Resolver.hs` can use it.
**Co-located proxy warning.** `newEnv` logs a startup warning whenever `allowSMPProxy = True` and `namesConfig = Just _`. RSLV is the first slow forwarded command; on a proxy host it can serialise other forwarded commands on the same proxy-relay session up to `rpcTimeoutMs` per cache miss. The warning is not a hard refusal because `[PROXY]` has no `enable: on/off` toggle — proxy is always on for every smp-server. `forkForwardedCmd` async dispatch is the longer-term fix, tracked as a follow-up; once the proxy role is gateable per-server, the warning can be tightened back to a refusal.
## Resolver subtree
New module tree at `src/Simplex/Messaging/Server/Names/`:
| Module | Contents |
|---|---|
| `Names.hs` | Façade — re-exports `NamesConfig`, `NamesEnv`, `ResolveError`, `resolveName`, `newNamesEnv`, `closeNamesEnv`. |
| `Names/Resolver.hs` | All types + cache + in-flight + `resolveName`. Helpers exported directly (no `.Internal` per codebase convention). **Test seam**: `NamesEnv` holds `ethCall` as a function value, so tests construct stubs via `newNamesEnvWith`. |
| `Names/Eth/RPC.hs` | `EthRpcEnv`; `ethCallReal` via `http-client` + `withResponse` + `brReadSome rpcMaxResponseBytes`. JSON-RPC error / HTTP error split. `rpcMaxConcurrency` semaphore. `Authorization` header from `rpcAuth`. |
| `Names/Eth/SNRC.hs` | `EthAddress`, Keccak-256 namehash via `crypton`'s `Crypto.Hash.Algorithms.Keccak_256` (mirroring `Crypto.hs:10231025` for SHA3), hand-rolled bounded Solidity ABI codec, `getRecord` with zero-owner detection. **Ethereum's Keccak ≠ NIST SHA3-256.** |
**ABI codec invariants**, enforced before any allocation: `offset + 32 ≤ buf.length`; `offset + 32 + length ≤ buf.length`; `offset ≥ headEnd` (no backward jumps); every length ≤ per-field cap; `string[]` outer length × 32 ≤ buf.length; recursion depth ≤ 2; `uint256 → Int64` rejects if any high 24 bytes non-zero; UTF-8 via `decodeUtf8'` returns `EthDecodeErr`.
**Zero-owner → `NotFound`**: ENS-style resolvers return zeroed records for non-existent names. After ABI decode, if `nrOwner == NameOwner (B.replicate 20 0)` return `Left NotFound`.
**Errors.**
```haskell
data ResolveError = NotFound | EthHttpErr | EthRpcErr { rpcCode :: Int, rpcMessage :: Text }
| EthDecodeErr | TimedOut
```
All collapse to `ERR AUTH`. `EthRpcErr` carries JSON-RPC `error` object — method-not-found (SNRC not deployed at `snrc_address`) is logged immediately on the first error after a recent success: `logError "NAMES: JSON-RPC error from endpoint — check snrc_address: <code> <message>"`. No automatic retry.
**Cache.** TTL + FIFO eviction. `TVar (OrdPSQ LookupKey Word64 NameRecord, Int)` — priority = monotonic-ns at insert; the `Int` is running byte count. `cacheLookup` is one STM transaction (read, expiry-check, expired-delete-with-byte-decrement). `cacheInsert` is one STM transaction: while `size > cacheMaxEntries` OR `bytes + sizeOf(rec) > cacheMaxBytes`, `minView` to drop oldest, then `insert`. Byte counter prevents `100 000 × 9 KB ≈ 900 MB` worst-case blow-up.
**Request coalescing** (async-exception safe via `E.mask`):
```haskell
resolveName env bs = do
let k = LookupKey bs
now <- getMonotonicTimeNSec
atomically (cacheLookup env k now) >>= \case
Just rec -> incStat (rslvCacheHits ...) $> Right rec
Nothing -> do
incStat (rslvCacheMiss ...)
ticket <- atomically $ TM.lookup k (inflight env) >>= \case
Just mv -> pure (Waiter mv)
Nothing -> newEmptyTMVar >>= \mv -> TM.insert k mv (inflight env) $> Leader mv
case ticket of
Waiter mv -> atomically (readTMVar mv)
Leader mv -> E.mask $ \restore -> do
r <- restore (fetchOnceTimed env bs)
`E.catch` \(e :: E.SomeException) -> pure (Left (mapEthErr e))
atomically $ putTMVar mv r >> TM.delete k (inflight env)
case r of Right rec -> atomically (cacheInsert env k now rec); Left _ -> pure ()
pure r
fetchOnceTimed env bs =
System.Timeout.timeout (rpcTimeoutMs (config env) * 1000) (fetchOnce env bs) >>= \case
Just r -> pure r
Nothing -> pure (Left TimedOut)
```
`E.mask` ensures `putTMVar + TM.delete` runs even on async exception; `fetchOnceTimed` runs under `restore` so it remains interruptible. Waiters always see a value; the in-flight TMap entry is always removed.
`fetchOnce`, `mapEthErr`, `scrubUrl`, `cacheLookup`, `cacheInsert` are internal to `Resolver.hs`. `getMonotonicTimeNSec` from `GHC.Clock` — first monotonic-clock use in the codebase; clock-jump safe.
**STM contention.** Cache hits are read-only `readTVar` — STM scales. Cache writes under sustained miss traffic can retry; `CacheSpec` asserts < 5% retry at 4 readers + 1 writer @ 1k RPS. If observed higher, swap `TVar` for `IORef` + `atomicModifyIORef'`.
**Multicoin and text records** are not in `NameRecord`. If Part 1 contract returns them from `getRecord`, extend `NameRecord` and the wire-size budget. **Confirm with Part 1 author before implementing `Eth/SNRC.hs`.**
## Configuration
`ServerConfig` (`Env/STM.hs:142`) gains one field `namesConfig :: Maybe NamesConfig`. `Env` (`Env/STM.hs:261`) gains `namesEnv :: Maybe NamesEnv`. `newEnv` constructs it after `proxyAgent` (line 605) with the co-location guard.
```haskell
data NamesConfig = NamesConfig
{ ethereumEndpoint :: Text -- http(s), no userinfo, explicit port required
, snrcAddress :: NameOwner -- 20 bytes
, rpcAuth :: Maybe RpcAuth -- required when https & non-loopback host
, cacheSeconds :: Int -- 300
, cacheMaxEntries :: Int -- 100000
, cacheMaxBytes :: Int -- 67108864 (64 MB)
, rpcTimeoutMs :: Int -- 3000
, rpcMaxResponseBytes :: Int -- 262144 (256 KB)
, rpcMaxConcurrency :: Int -- 8
}
data RpcAuth = AuthBearer Text | AuthBasic Text Text
```
INI parsing in `Server/Main.hs`:
- `validateUrl` (using new `network-uri` dep): accepts only http(s), non-empty host, **explicit port** (rejects `http://localhost` defaulting to 80 while Reth is on 8545), no userinfo, no query/fragment. Rejects `https://...` without `rpc_auth` when host is non-loopback. On rejection: `logError` + `exitFailure`.
- `parseEthAddr`: accepts `0x[0-9a-fA-F]{40}` and the same without `0x`. Mixed-case → verify EIP-55 checksum and reject mismatch (catches typos).
- `parseRpcAuth`: reads optional `rpc_auth` key; format `bearer <token>` or `basic <user>:<pass>`.
- `scrubUrl`: strips userinfo from all log lines mentioning the endpoint, including inside `mapEthErr`.
- Transition-aware error logging: log immediately on first error after a recent success, then at most hourly while persisting + summary at every stats reset.
Default INI template (`Server/Main/Init.hs`, after `[PROXY]`):
```
[NAMES]
# Public-namespace resolution (SNRC on Ethereum).
# Requires an Ethereum JSON-RPC endpoint (Reth+Nimbus). See deployment guide.
# Cannot be combined with [PROXY] enable: on by default — see allow_dangerous_colocation.
# Restart required to change settings.
enable: off
# Same-host:
# ethereum_endpoint: http://127.0.0.1:8545
# Central Reth via Caddy:
# ethereum_endpoint: https://eth.simplex.chat:443
# rpc_auth: basic <username>:<password>
# snrc_address: 0x0000000000000000000000000000000000000000
# cache_seconds: 300
# cache_max_entries: 100000
# cache_max_bytes: 67108864
# rpc_timeout_ms: 3000
# rpc_max_response_bytes: 262144
# rpc_max_concurrency: 8
# allow_dangerous_colocation: off
```
Upgrade from a pre-v6.6 INI: missing `[NAMES]` section → disabled. No operator action required.
## Operator deployment
Two supported topologies. smp-server is agnostic — only `ethereum_endpoint` changes.
**Topology A (same-host)**: smp-server, Caddy (optional), Reth, Nimbus all on one box. `ethereum_endpoint: http://127.0.0.1:8545`.
**Topology B (central Reth, N smp-server hosts — recommended for fleets)**: one operator runs one eth host with Reth+Nimbus behind Caddy on public HTTPS. Each smp-server has its own credential.
```mermaid
flowchart LR
subgraph eth-host
Caddy["Caddy<br/>(public :443, basic auth)"]
Reth["Reth<br/>(127.0.0.1:8545)"]
Nimbus["Nimbus"]
Caddy --> Reth
Nimbus -- Engine API (jwt.hex) --> Reth
end
subgraph smp-host-1
S1["smp-server #1"]
end
subgraph smp-host-N
SN["smp-server #N"]
end
S1 -- HTTPS + Authorization --> Caddy
SN -- HTTPS + Authorization --> Caddy
Reth <-- Ethereum p2p --> internet
Nimbus <-- beacon sync --> internet
```
Sharing one Reth across **multiple operators** is **not** supported — collapses the RFC's two-server resolution privacy.
**Reth + Nimbus**: Reth (execution layer) holds Ethereum state on ~260 GB pruned NVMe; Nimbus (consensus light client) follows beacon-chain headers. Paired via Engine API on `127.0.0.1:8551` with a shared `jwt.hex`. Recommended Reth flags:
```bash
reth node \
--http.addr 127.0.0.1 \
--http.api eth \ # only eth namespace
--rpc.gascap 50000000 \ # cap gas per eth_call
--rpc.max-response-size 5242880 \ # 5 MB
--http.corsdomain none \
--authrpc.jwtsecret /opt/eth/jwt.hex \
--authrpc.addr 127.0.0.1 --authrpc.port 8551
```
**Caddy + Let's Encrypt + Basic auth** (Topology B):
```caddy
eth.simplex.chat {
basicauth {
smp-server-1 $2a$14$<bcrypt-hash-1>
smp-server-2 $2a$14$<bcrypt-hash-2>
}
log { format filter { wrap json; fields { request>headers>Authorization delete } } }
reverse_proxy 127.0.0.1:8545
}
```
Caddy auto-fetches Let's Encrypt cert. Each smp-server has its own credential; revoking one = delete the line. `Authorization` stripped from access logs. Port 80 needed for the ACME HTTP-01 challenge (use TLS-ALPN-01 or DNS-01 to drop it). The threat being defended against is DoS (SNRC state is public); mTLS would be overkill. WireGuard/Tailscale are alternative network-layer approaches — both compatible with the plan.
**Capacity.** One Reth+Nimbus box handles a realistic operator fleet by 101000× margin. Per-smp-server peak RSLV ≈ 1700 RPS (pessimistic); cache hit rate ≥ 95% → ~85 RPS cache miss per smp-server; 10 smp-servers → ~850 RPS aggregate cache miss reaching Reth; Reth `eth_call` throughput on warm NVMe ≈ 1k10k RPS. Sizing: 8 vCPU, 32 GB RAM, 1 TB NVMe is comfortable. Scale-out path: more Reth+Nimbus pairs, smp-servers round-robin or shard.
## Implementation
**Order**:
1. Protocol: party/SParty/PartyI, RSLV+tag, NAME+tag, NameRecord + helpers, version constants in `Transport.hs`.
2. `verifyTransmission`/`verifyLoadedQueue`/`verifyQueueTransmission` `forwarded :: Bool` flag + `vc SResolver` clauses.
3. Forwarded whitelist + `processCommand` branch + `incStat` move to `Stats.hs`.
4. Env plumbing: `Server/Env/STM.hs`, `Server/Main.hs` INI parse, `Server/Main/Init.hs` template.
5. Resolver subtree: `Eth/SNRC.hs``Eth/RPC.hs``Resolver.hs`.
6. `NameResolverStats` sub-record + CSV log + Prometheus `names =` block.
7. Replace stub in (3) with real `resolveName`.
8. Tests.
9. `protocol/simplex-messaging.md`: header version line 1 (`19 → 20`), sentence at line 86, version-history list (lines 93105) v20 entry, TOC (lines 2568) "Resolver commands" subsection, new section with ABNF + byte layout + error semantics, "Router security requirements" paragraph about names-role outbound HTTP, cross-ref `Transport.hs:226`.
10. `CHANGELOG.md`: v6.6 entry.
**Cabal** (`simplexmq.cabal`): bump `version: 6.6.0.0`. Add to `if !flag(client_library)` block: `http-client >=0.7 && <0.8`, `http-client-tls >=0.3 && <0.4`, `network-uri >=2.6 && <2.7`, `psqueues >=0.2.7 && <0.3`. Expose 4 new `Server.Names.*` modules in the same block. `crypton` already provides `Keccak_256`.
**Files changed**:
| File | Change |
|---|---|
| `Protocol.hs` | Resolver party + RSLV/NAME tags + version guards; `NameRecord` + newtypes + smart ctors; `nameRecBytes`/`parseNameRec`/`smpListPUpTo` helpers (no Encoding NameRecord instance); `LookupKey` parser-side cap |
| `Transport.hs` | `namesSMPVersion = 20`; bump current/proxied SMP versions |
| `Server.hs` | Thread `forwarded :: Bool`; `vc SResolver` clauses; whitelist (2132); Resolver branch in `processCommand` (1481); `closeServer` calls `closeNamesEnv`; CSV log (579618); **remove** local `incStat` |
| `Server/Env/STM.hs` | `namesConfig` field; `namesEnv` field; `newEnv` constructs `NamesEnv` with co-location guard |
| `Server/Main.hs` | `[NAMES]` parse: `validateUrl`/`parseEthAddr`/`parseRpcAuth`; `scrubUrl` in logs |
| `Server/Main/Init.hs` | `[NAMES]` block in default INI |
| `Server/Stats.hs` | `incStat` moved here + exported; `NameResolverStats` sub-record + helpers; `rslvStats` field |
| `Server/Prometheus.hs` | `names =` metric block |
| `Server/Names.hs` (new) | Façade re-exports |
| `Server/Names/Resolver.hs` (new) | All resolver types + cache + coalescing + `fetchOnceTimed` + `newNamesEnv[With]` + `closeNamesEnv` |
| `Server/Names/Eth/RPC.hs` (new) | `EthRpcEnv`, `ethCallReal` with bounded body + concurrency semaphore + `Authorization` header |
| `Server/Names/Eth/SNRC.hs` (new) | `EthAddress`, Keccak namehash, bounded ABI (8 invariants), `getRecord` with zero-owner detection |
| `simplexmq.cabal` | Bump `6.6.0.0`; 4 new deps + 4 new modules in `if !flag(client_library)` block |
| `protocol/simplex-messaging.md` | Header version, version-history v20 entry, new "Resolver commands" section |
| `CHANGELOG.md` | v6.6 entry |
## Testing
`tests/SMPNamesTests/` registered in `tests/Test.hs:112151`. Build only when `client_library = False`.
1. **ProtocolEncodingSpec**`nameRecBytes``parseNameRec` round-trip; oversized fields rejected at parse; combined-list cap 8 enforced; negative `nrExpiry` rejected; canonical encoding byte-stable.
2. **MaxSizeSpec** — max `NameRecord` encodes ≤ ~9 KB; `encodeTransmission v ≤ paddedProxiedTLength - 2`; `cbEncrypt` succeeds.
3. **CommandTagSpec**`"RSLV"`/`"NAME"` parse; v < 20 sessions reject `RSLV_` at parameter parser.
4. **ForwardedGateSpec** — direct RSLV → `CMD PROHIBITED`; forwarded RSLV reaches handler.
5. **ForwardedRslvSpec** — RSLV wrapped in PFWD reaches the handler end-to-end. **Test infra cost**: first protocol-level PFWD test; budget for `runProxiedSmpCommand` helper performing `PRXY`/`PKEY`/`PFWD` manually.
6. **CacheSpec** — hit avoids RPC; TTL expiry forces re-fetch; bytes cap evicts before entries cap on large records; concurrent same-key callers issue one RPC; leader exception → all waiters get `Left _`, TMap entry removed; leader async-cancel → cleanup STM still runs.
7. **AbiSpec** — encode/decode against pinned fixtures (`tests/fixtures/snrc/`); QuickCheck fuzz on random buffers ≤ `rpcMaxResponseBytes` must never crash.
8. **NamehashSpec** — Keccak-256 reference vectors; assert Keccak ≠ SHA3-256.
9. **MockRpcSpec** — fake HTTP server; missing → `EthHttpErr`; slow → `TimedOut`; multi-GB body truncated → `EthDecodeErr`. `rpcAuth = AuthBasic` sends correct header.
10. **Uint256OverflowSpec**`expiry > Int64.maxBound``EthDecodeErr`.
11. **ZeroOwnerSpec**`owner = 0x000...000``NotFound`.
12. **StartupGuardSpec**`allowSMPProxy + names.enable` aborts; `allow_dangerous_colocation = on` starts with warning.
13. **UrlValidationSpec** — userinfo/scheme/host/port edge cases; rejects `https://` without `rpc_auth` for non-loopback.
14. **EipChecksumSpec**`parseEthAddr` accepts lower/upper; verifies mixed-case checksum; rejects typos.
15. **AbiBoundsSpec** — each of 8 ABI invariants triggers `EthDecodeErr` without crash/allocation blow-up.
Integration against real Reth+Nimbus mainnet deferred to ops.
## Threat model, scope, coordination
| Actor | Can | Cannot |
|---|---|---|
| Name server | See lookup-key bytes; see query timing; see Eth endpoint URL (operator-self) | See client IP/session; correlate clients across queries |
| Compromised Eth endpoint | Poison this server's cache for one TTL window; see every lookup key the server queries | Bypass two-server agreement (client-side, out of scope) |
| Adversarial client (high-rate unique keys) | Cache-thrash DoS; fill `Manager` connection pool up to `managerConnCount = 8` | Bypass `rpcMaxResponseBytes` or `fetchOnceTimed` |
| Adversarial proxy (slow inner RSLVs) | Block other forwarded commands on that proxy connection up to `rpcTimeoutMs` per miss | Affect other proxy connections |
| Operator with footgun config (https no auth, public Eth RPC) | (rejected at startup, or operator-acknowledged data leak) | — |
Mitigations: caching + coalescing + `rpcTimeoutMs` + `rpcMaxResponseBytes` + `rpcMaxConcurrency`; co-location refused at startup; URL validation; Caddy + auth in front of Reth; Reth's own gas/size caps. Timing side-channels (cache-hit vs miss latency) not mitigated — flagged for post-MVP. State proofs deferred to post-MVP per RFC.
**Cross-repo coordination.** The `simplex-chat` `ep/namespace` branch currently contains only the RFC commit — no agent-side wire-format code yet. This plan's wire format is validated only by simplexmq's own tests until a matching agent PR lands (structurally weak — encoder/decoder bugs are mutually consistent with themselves). Coordinate with the agent-side implementer **before merging** on: exact `NameRecord` field order and types; `LookupKey` namespace-prefix convention; error-code semantics; Part 1 SNRC contract `getRecord` ABI surface.
@@ -0,0 +1,180 @@
# SMP router message storage
## Problem
Currently SMP routers store all queues in router memory. As the traffic grows, so does the number of undelivered messages. What is worse, Haskell is not avoiding heap fragmentation when messages are allocated and then de-allocated - undelivered messages use ByteString and GC cannot move them around, as they use pinned memory.
## Possible solutions
### Solution 1: solve only GC fragmentation problem
Move from ByteString to some other primitive to store messages in memory long term, e.g. ShortByteString, or manage allocation/de-allocation of stored messages manually in some other way.
Pros: the simplest solution that avoids substantial re-engineering of the router.
Cons:
- not a long term solution, as memory growth still has limits.
- may be ineffective, as it introduces additional copying of bytes.
### Solution 2: move message storage to hard drive
Use files or RocksDB to store messages.
Pros:
- much lower memory usage.
- no message loss in case of abnormal router termination (important until clients have delivery redundancy).
- this is a long term solution, and at some point it might need to be done anyway.
Cons:
- substantial re-engineering costs and risks.
- metadata privacy. Currently we only save undelivered messages when router is restarted, with this approach all messages will be stored for some time. this argument is limited, as hosting providers of VMs can make memory snapshots too, on the other hand they are harder to analyze than files. On another hand, with this approach messages will be stored for a shorter time.
#### RocksDB and other key-value stores
The downside of any key-value stores is that they don't seem to have efficient primitives for sequential delivery. While sequential delivery can be modelled with linked lists, they would require 1 key insert (on send), 3 key updates (1 update to update queue data on send, 1 update of the last message to point to the next, 1 update on delivery or message expiration) and 1 key deletion (on delivery or message expiration) for each delivered message.
This might result in substantial write amplification and compacting costs.
In general, tree structures that are efficient for quick lookups and updates, given approximately fixed value size, are inefficient for modelling queues.
#### Files
The upside of files is that they are well suited for sequential delivery and don't result in the same churn, with careful design, as trees do.
The downside of filesystem is that it does not scale well with the large number of files in a folder, so queues will have to be spread across multiple folders, following tree-like structure.
I could not find an available library that efficiently models sequential delivery in highly concurrent environment.
A possible design could be the following.
##### Queue folder and files
Each message queue is stored in its own folder (see below on folder locations). Folder would contain these files:
- `messages.abcd.log` - the file that is used to read messages from, sequentially
- `messages.efgh.log` - the optional file that is used to write messages, in case it is different from read file.
- `queue.log` - append-only file where the last line represents the current queue state
- `queue.timestamp.log` - previous states of queue.log file
Each line in "queue.log" file has this syntax
```abnf
queueLogLine =
%s"read_file=" base64
%s"read_msg=" digits
%s"read_byte=" digits
%s"write_file=" base64
%s"write_msg=" digits
```
When queue is first requested by the router:
```c
if queue folder exists:
read queue state from last line of queue.log
if queue.log contained more than one line: // compaction
copy queue.log to queue.timestamp.log
write one line queue state to queue.log
else:
create queue folder
create messages.abcd.log (abcd is some random string)
read_msg = 0
read_byte = 0
create queue.log with one line: "read_file=abcd read_msg=0 read_byte=0 write_files=abcd write_msg=0"
open read_file in ReadMode and seek to read_byte position
nextReadByte = read_byte
nextReadMsg = read_msg
open write_file in AppendMode
```
When message is added to the queue (assumes that queue state is loaded to router memory, if not the previous section will be done first):
```c
if write_msg > max_queue_messages:
return quota error
else if write_msg = max_queue_messages:
add quota_exceeded message to write_file
update queue state: write_msg += 1
append updated queue state to queue.log
else
// It is required that `max_queue_messages < max_file_messages`,
// so that we never need more than one additional write file.
if write_msg >= max_file_messages: // queue file rotation
create messages.efgh.log // efgh is some random string
update queue state: write_file=efgh write_msg=0 // read file remains the same as it was
append updated queue state to queue.log
copy queue.log to queue.timestamp.log
// `old` needs to be defined to limit the number and storage duration,
// preserving not more than N files, and not more than M days files, "and then some"
// (that is if the queue has high churn, we have file from M days before in any case, for any debugging).
delete `old` `queue.timestamp.log` files
write one line queue state to queue.log // compaction
add message to write_file
update queue state: write_msg += 1
append updated queue state to queue.log
```
The above algorithm assumes `max_queue_messages < than max_file_messages`, so that we never need more than one write file.
When message is delivered, it is simply read from the read queue, queue state does not change yet:
```c
if nextReadMsg > read_msg:
deliver cached message, no need to read it again
else
read message from read_file handle
nextReadMsg = read_msg + 1
nextReadByte = current position in file
```
When message delivery is acknowledged, the read queue needs to be advanced, and possibly switched to read from the current write queue:
```c
if nextReadByte == read_byte:
return error // nothing was delivered
else if nextReadByte = EOF:
// end of file is reached, possibly some other condition,
// but it should allow changing max_file_messages on server restart
currReadFile = read_file
read_file = write_file
read_msg = 0
read_byte = 0
append updated queue state to queue.log
delete currReadFile
else
read_msg += 1
read_byte = nextReadByte
// `seek` should not be necessary, as the handle is already at nextReadByte position here
// seek to read_byte
append updated queue state to queue.log
```
The above algorithm delegates the problem of compaction and fragmentation management to file system, that is very optimized for such scenarios.
Also, read and write files will grow to almost a constant size, so the space they used may be re-used.
An important consideration is that writes to queue.log and message.log files and queue state modifications have to be sequential, without concurrency - it can be managed with the usual locks.
##### Queue folders structure
Most Linux systems use EXT4 filesystem where the file lookup time scales linearly to the number of files. While alternatives with logarithmic lookup time exist (XFS), they may be very complex to configure on the existing systems.
So storing all queue folders in one folder won't scale.
To solve this problem we could use recipient queue ID in base64url format not as a folder name, but as a folder path, splitting it to path fragments of some length. The number of fragments can be configurable and migration to a different fragment size can be supported as the number of queues on a given router grows.
Currently, queue ID is 24 bytes random number, thus allowing 2^192 possible queue IDs. If we assume that a router must hold 1b queues, it means that we have ~2^162 possible addresses for each existing queue. 24 bytes in base64 is 32 characters that can be split into say 8 fragments with 4 characters each, so that queue folder path for queue with ID `abcdefghijklmnopqrstuvwxyz012345` would be:
`/var/opt/simplex/messages/abcd/efgh/ijkl/mnop/qrst/uvwx/yz01/2345`
The maximum theoretic number of the folders on the 1st level is 64^4, or 2^24 ~ 16m - this is probably still a large number of subfolders for EXT4. Given that addresses are random, all the possible combinations in the first folder can be used with a large number of queues.
So we could use an unequal split of path, two letters each and the last being long:
`/var/opt/simplex/messages/ab/cd/ef/ghijklmnopqrstuvwxyz012345`
The first three levels in this case can have 4096 subfolders each, and it gives 68b possible subfolders (64^2^3), so the last level will be sparse in case of 1b queues on the router. So we could make it 4 levels with 2 letters to never think about it, accounting for a large variance of the random numbers distribution:
`/var/opt/simplex/messages/ab/cd/ef/gh/ijklmnopqrstuvwxyz012345`
+162
View File
@@ -0,0 +1,162 @@
# Sharing protocol ports with HTTPS
Some networks block all ports other than web ports, including port 5223 used for SMP protocol by default. Running SMP routers on a common web port 443 would allow them to work on more networks. The routers would need to provide an HTTPS page for browsers (and probes).
## Problem
Browsers and tools rely on system CA bundles instead of certificate pinning.
The crypto parameters used by HTTPS are different from what the protocols use.
Public certificate providers like LetsEncrypt can only sign specific types of keys and Ed25519 isn't one of them.
This means a router should distinguish browser and protocol clients and adjust its behavior to match.
## Solution
`tls` package has a server hook that allows producing a different set of `TLS.Credentials` according to a client-provided "Server Name Indication" extension.
Since LE certificates are only handed out to domain names, TLS client will be sending the SNI.
However client transports are constructed over connected sockets and the SNI wouldn't be present unless explicitly requested.
When a client sends SNI, then it's a browser and web credentials should be used.
Otherwise it's a protocol client to be offered the self-signed ca, cert and key.
When a transport colocated with a HTTPS, its ALPN list should be extended with `h2 http/1.1`.
The browsers will send it, and it should be checked before running transport client.
If HTTP ALPN is detected, then the client connection is served with HTTP `Application` instead (the same "router information" page).
If some client connects to router IP, doesn't send SNI and doesn't send ALPN, it will look like a pre-handshake client.
In that case a router will send its handshake first.
This can be mitigated by delaying its handshake and letting the probe to issue its HTTP request.
## Implementation plan
An unmodified client should be able to use protocols on port 443 right away.
The switchover happens inside `runTransportServerState` before `runClient`:
```haskell
runServer (tcpPort, ATransport t) = do
-- ...
runTransportServerState_ ss started tcpPort serverParams tCfg $ \socket h -> do -- expose raw socket for warp-tls internals to attach
negotiated <- getSessionALPN
if allowHTTP t && isHTTP negotiated -- only attempt the switch for the TLS transport
then runHTTP socket (tlsContext h)-- ... collect data and produce values needed to run WAI Application
else runClient serverSignKey t h `runReaderT` env -- performs serverHandshake etc as usual
```
The web app and router live outside, so `runHttp` has to be provided by the `runSMPServer` caller.
Additonally, Warp is using its `InternalInfo` object that's scoped to `withII` bracket.
```haskell
runServer ini = do
-- ...
runWebServer ini ServerInformation {config, information} $ if sharedHttps then Nothing else webHttpsParams -- suppress serving https
if sharedHttps
then withRunHTTP staticFilesPath \attachStatic -> runSMPServer cfg (Just attachStatic) -- provide wrapped application runner
else runSMPServer cfg Nothing
```
### Upstream
The implementation relies on a few modification to upstream code:
- `warp-tls`: The library provides `httpOverTls`, but it wants to do handshake itself.
Since we have to do the handshake to switch on ALPN, the setup function has to be split.
This is a resonable change that may be upstreamed and nothing blocks us from using the recent version.
- `warp`: Only the re-export of `serveConnection` is needed.
Unfortunately the most recent `warp` version can't be used right away due to dependency cascade around `http-5` and `auto-update-2`.
So a fork containing the backported re-export has to be used until the dependencies are refreshed.
### TLS.ServerParams
When a router has port sharing enabled, a new set of TLS params is loaded and combined with transport params:
```haskell
newEnv config = do
-- ...
tlsServerParams <- loadTLSServerParams caCertificateFile certificateFile privateKeyFile (alpn transportConfig)
sharedServerParams <- forM ((,) <$> sharedHttpsCredentials config <*> alpn transportConfig) $ \((chain, key), alpn) ->
let ca = Nothing -- It is possible to provide CA certificate, but it is typical for web server to use combined certificate chains
loadHTTPSServerParams tlsServerParams ca chain key alpn
```
`loadHTTPSServerParams` extends params with:
1. `onALPNClientSuggest` hook gets `["h2", "http/1.1"]` added to the ALPN list which is now required.
2. `onServerNameIndication` hook added, which upon detecting client SNI prepends the web credentials.
3. `sharedCredentials = T.Credentials []` should be done to prevent transport credentials confusing browsers.
But that aborts key exchange somewhere in tls internals, so disabled for now.
As a workaround, another set of dummy credentials can be provided in the hope that any sane browser would reject them.
Like, RC4 ciphers, "impossible" digest combination, etc.
### supportedParameters
TLS certificate chains provided by LetsEncrypt use ECDSA/P256 and that requires extending `supportedParameters` with things disabled in transports:
```haskell
browserCiphers =
[ TE.cipher_TLS13_AES128CCM8_SHA256
, TE.cipher_ECDHE_ECDSA_AES128CCM8_SHA256
, TE.cipher_ECDHE_ECDSA_AES256CCM8_SHA256
]
browserGroups =
[ T.P256
]
browserSigs =
[ (T.HashSHA256, T.SignatureECDSA),
(T.HashSHA384, T.SignatureECDSA)
]
```
This may not be enough for other certificate providers.
## Configuration
> XXX: This is for the current implementation and should be updated.
Web certificate chain is picked up from the WEB section:
```ini
[TRANSPORT]
port: 443
[WEB]
https: 443
cert: /etc/opt/simplex/web.cert
key: /etc/opt/simplex/web.key
# Alternatively, with a proper access configuration, the paths can point to the LE creds directly:
# cert: /etc/letsencrypt/live/smp.hostname.tld/fullchain.pem
# key: /etc/letsencrypt/live/smp.hostname.tld/privkey.pem
```
When `TRANSPORT.port` matches `WEB.https` the transport router becomes shared.
Perhaps a more desirable option would be explicit configuration resulting in additional transported to run:
```ini
[TRANSPORT]
port: 5223 ; pure protocol transport
# control_port: 5224
shared_port: 443 ; variant 1: register in TRANSPORT
[WEB]
https: 443
cert: /etc/opt/simplex/web.cert
key: /etc/opt/simplex/web.key
# transport: on ; variant 2:
```
## Caveats
Serving static files and the protocols together may pose a problem for those who currently use dedicated web servers as they should switch to embedded http handlers.
As before, using embedded HTTP server is increasing attack surface.
Users who want to run everything on a single host will have to add an extra IP address and bind routers to specific IPs instead of 0.0.0.0.
An amalgamated router binary can be provided that would contain both SMP and XFTP routers, where transport will dispatch connections by handshake ALPN.
## Alternative: Use transports routable with reverse-proxies
An "industrial" reverse proxy may do the ALPN routing, serving HTTP by itself and delegating `smp` and `xftp` to protocol servers.
Same with the `websockets`.
Since this in effect does TLS termination, the protocol routers will have to rely on credentials from protocol handshakes.
@@ -0,0 +1,16 @@
# Expiring messages in journal storage
## Problem
The journal storage routers recently migrated to do not delete delivered or expired messages, they only update pointers to journal file lines. The messages are actually deleted when the whole journal file is deleted (when fully deleted or fully expired).
The problem is that in case the queue stops receiving the new messages then writing of messages won't switch to the new journal file, and the current journal file containing delivered or expired messages would never be deleted.
## Solution
Remove current journal file and update queue_state.log during message expiration of "idle" queue (that is, without any new messages received or delivered within 3 hours) in case when:
- the queue is "empty" after the expiration
- the queue contains only quota marker(s), in which case move them to a new journal file and update the queue_state accordingly. Quota markers can be kept indefinitely to prevent writing the new messages to the dormant queues that reached capacity, so it's important to handle this case.
Also remove current journal file when the queue is opened in case it is empty (as it would not be ever expired in case it remains empty), and also update queue_state.log
+416
View File
@@ -0,0 +1,416 @@
# Fix subQ deadlock: blocking writeTBQueue inside connLock
## Problem
Users report that message reception silently and permanently stops across all connections, with no error alerts. The app appears functional but no messages arrive. Recovery requires restart.
Root cause: a deadlock between worker threads holding `connLock` and the `agentSubscriber` (sole `subQ` reader).
### The deadlock mechanism
`subQ` (`TBQueue ATransmission`, capacity 4096 on mobile / 1024 on desktop) is the single pipeline between the agent layer and the chat layer. The `agentSubscriber` thread (`Commands.hs:4373`) is its **sole reader**.
Three code sites hold `connLock` and call blocking `writeTBQueue subQ` without a fullness check. When `subQ` is full, these block while holding the lock. If `agentSubscriber` simultaneously needs the same `connLock` (via `sendMessagesB_``withConnLocks`), it blocks too — creating a circular wait:
- **Worker**: holds `connLock(X)`, waits for `subQ` space (needs `agentSubscriber` to read)
- **agentSubscriber**: sole `subQ` reader, waits for `connLock(X)` (needs worker to release)
- **Result**: permanent silent deadlock — no exception, no alert, all connections blocked
### Confirmed deadlock scenarios
**Scenario 1**: Delivery worker during queue rotation test
```
Delivery worker: agentSubscriber (sole subQ reader):
withConnLock(X) [2187] readTBQueue subQ → processAgentMessageConn
...DB operations... → sendPendingGroupMessages (on CON/SENT/QCONT)
notify → writeTBQueue subQ [2238] → batchSendConnMessages → deliverMessagesB
[BLOCKED — subQ full] → withAgent sendMessagesB [synchronous]
→ sendMessagesB_ → withConnLocks({..X..}) [1708]
[BLOCKED — connLock(X) held]
```
**Scenario 2**: Async command worker during message ACK with notification
```
Async cmd worker: agentSubscriber (sole subQ reader):
tryWithLock "ICAck" [1930→1824] readTBQueue subQ → processAgentMessageConn
→ withConnLock(X) → sendPendingGroupMessages
→ ack → ackQueueMessage [1899] → sendMessagesB_ → withConnLocks({..X..})
→ sendMsgNtf [2381] [BLOCKED — connLock(X) held]
→ writeTBQueue subQ [2386]
[BLOCKED — subQ full]
```
**Scenario 3**: Synchronous `ackMessage'` API (same mechanism as Scenario 2 but from external API caller)
```
ackMessage' caller: agentSubscriber (sole subQ reader):
withConnLock(X) [2254] → sendMessagesB_ → withConnLocks({..X..})
→ ack → ackQueueMessage [2267] [BLOCKED — connLock(X) held]
→ sendMsgNtf [2381]
→ writeTBQueue subQ [2386]
[BLOCKED — subQ full]
```
### ConnId overlap verified
No guard prevents a connection undergoing queue rotation (AM_QTEST_) or ACK processing from being included in `sendMessagesB_`'s batch. During these operations, the connection has `connStatus == ConnReady`, passing all filters in `memberSendAction`.
### Cascade amplification
Once any single deadlock triggers, `subQ` never drains. ALL other threads that attempt `writeTBQueue subQ` block progressively — their locks are held forever too. The entire threading system freezes within seconds.
### Affected code sites (blocking `writeTBQueue subQ` inside `connLock`)
| Site | File | Lock line | Write line | Events written |
|------|------|-----------|------------|----------------|
| `runSmpQueueMsgDelivery::notify` | Agent.hs | 2187 | 2238 | SWITCH SPCompleted, ERR INTERNAL |
| `runSmpQueueMsgDelivery::internalErr/notifyDel` | Agent.hs | 2187 | 2238 (via notifyDel→notify) | ERR INTERNAL + delMsg |
| `ackQueueMessage::sendMsgNtf` | Agent.hs | 2254 or 1930 | 2386 | MSGNTF |
### Safe patterns that already exist in the codebase
1. **`isFullTBQueue` + pending TVar** (used at `runCommandProcessing` lines 1782-1784/1937, and `runProcessSMP` lines 3027-3029/3216):
```haskell
-- Before processing (e.g. line 1782):
pending <- newTVarIO []
-- During processing — safe notify (e.g. line 1937):
notify cmd =
let t = (corrId, connId, AEvt (sAEntity @e) cmd)
in atomically $ ifM (isFullTBQueue subQ) (modifyTVar' pendingCmds (t :)) (writeTBQueue subQ t)
-- After processing — flush (e.g. line 1784):
mapM_ (atomically . writeTBQueue subQ) . reverse =<< readTVarIO pending
```
2. **`nonBlockingWriteTBQueue`** (used at Client.hs:789, NtfSubSupervisor.hs:507):
```haskell
nonBlockingWriteTBQueue q x = do
sent <- atomically $ tryWriteTBQueue q x
unless sent $ void $ forkIO $ atomically $ writeTBQueue q x
```
Note: `nonBlockingWriteTBQueue` does NOT preserve ordering — the spawned background thread may complete out of order relative to subsequent direct writes from the same calling thread.
### Exhaustive proof: no other deadlock scenarios exist
All 15 `withConnLock` sites in Agent.hs were analyzed. Only 3 write to `subQ`:
| withConnLock site | Writes subQ? | Safe? |
|-------------------|-------------|-------|
| switchConnectionAsync' (899) | No | ✓ |
| setConnShortLinkAsync' (995) | No | ✓ |
| setConnShortLink' (1031) | No | ✓ |
| deleteConnShortLink' (1075) | No | ✓ |
| allowConnection' (1407) | No | ✓ |
| acceptContact' (1417) | No | ✓ |
| sendMessagesB_ (1708, `withConnLocks`) | No | ✓ |
| tryWithLock/runSmpCommand (1930) | Yes (1937) | ✓ — `isFullTBQueue` check |
| tryMoveableWithLock/runSmpCommand (1931) | Yes (1937) | ✓ — `isFullTBQueue` check |
| **runSmpQueueMsgDelivery AM_QTEST_ (2187)** | **Yes (2238)** | **✗ — DEADLOCK** |
| **ackMessage' (2254)** | **Yes (2386)** | **✗ — DEADLOCK** |
| switchConnection' (2298) | No | ✓ |
| abortConnectionSwitch' (2328) | No | ✓ |
| synchronizeRatchet' (2351) | No | ✓ |
| suspendConnection' (2390) | No | ✓ |
| **processSMP (3037)** | Yes (3216) | ✓ — `isFullTBQueue` check |
Note: `processSMP` (line 3037) holds `connLock` and its local `notify` (line 3216) writes to `subQ`, but it uses the safe `isFullTBQueue` pattern. Its `ack` (line 3196) uses `enqueueCmd` (DB-only), NOT `ackQueueMessage`. The actual `ackQueueMessage` runs later from the async command worker via ICAck/ICAckDel.
Other lock pairs checked — no circular dependencies:
- `connLock × DB MVar`: DB never acquires connLock
- `entityLock × connLock`: consistent ordering (entity first in chat, conn in agent)
- `connLock(X) × connLock(Y)`: single agentSubscriber thread, one `withConnLocks` at a time
---
## Deadlock call graph: agentSubscriber → connLock
All deadlock paths require `agentSubscriber` to synchronously acquire `connLock`. Exhaustive analysis shows that **every such path converges on a single agent function**: `sendMessagesB_``withConnLocks` (Agent.hs:1708). No other agent API function called synchronously from the agentSubscriber acquires connLock.
Verified (FACT): `ackMessageAsync``enqueueCommand` only (no connLock). `toggleConnectionNtfs` → no lock. `deleteConnectionAsync``deleteLock` not `connLock`. `joinConnectionAsync``withInvLock` not `connLock`.
Also verified (FACT): `Lock = TMVar Text` (Lock.hs:24) is **non-reentrant** — double acquisition on the same thread deadlocks.
### All 22 trigger paths
Every path goes through `deliverMessage`/`deliverMessages`/`deliverMessagesB``withAgent sendMessagesB``sendMessagesB_``withConnLocks`:
| # | Trigger | Chat function | ConnIds locked | Risk |
|---|---------|--------------|----------------|------|
| 1 | Group CON (Invitee) | `introduceToAll` → broadcast XGrpMemNew | **ALL member connIds** | **HIGHEST** |
| 2 | Group MSG XGrpLinkAcpt | `introduceToRemaining` → broadcast | **ALL member connIds** | **HIGHEST** |
| 3 | Group CON (Invitee) | `sendIntroductions` → batch intros to new member | new member connId | Medium |
| 4 | Group CON (Invitee) | `sendHistory` → batch to new member | new member connId | Medium |
| 5 | Group CON | `sendPendingGroupMessages` | member connId | Medium |
| 6 | Group SENT | `sendPendingGroupMessages` | member connId | Medium |
| 7 | Group QCONT | `sendPendingGroupMessages` | member connId | Medium |
| 8 | Group CON (PendingReview) | `introduceToModerators` → to moderators | moderator connIds | Medium |
| 9 | Group CON (PreMember) | `sendXGrpMemCon` → to host | host connId | Low |
| 10 | Group CON (PreMember) | `probeMatchingMemberContact` → probes + hashes | member + N matching connIds | Medium |
| 11 | Direct CON | `probeMatchingMembers` → probes + hashes | contact + N matching connIds | Medium |
| 12 | Direct JOINED | `sendAutoReply` | contact connId | Low |
| 13 | Group JOINED | `sendGroupAutoReply` | member connId | Low |
| 14 | Group INV | `sendXGrpMemInv` → to host | host connId | Low |
| 15 | Group INV (legacy) | `sendGrpInvitation` → to contact | contact connId | Low |
| 16 | Group MSG XGrpMemInv | `xGrpMemInv``sendGroupMemberMessage` | re-member connId | Low |
| 17 | Group MSG XGrpMemDel | `forwardToMember` | deleted member connId | Low |
| 18 | Group MSG XGrpLinkMem | `probeMatchingMemberContact` | member + N matching connIds | Medium |
| 19 | Group MSG (dup relay) | `saveGroupRcvMsg` error → `sendDirectMemberMessage` | forwarder connId | Low |
| 20 | SFDONE | `sendFileDescriptions` → to recipients | recipient connIds | Medium |
| 21 | Group MSG XGrpLinkAcpt | `sendHistory` → to accepted member | accepted member connId | Medium |
| 22 | Direct MSG (autoAccept) | `autoAcceptFile` → inline accept reply | contact connId | Low (test-only config) |
### Key observations
1. **Single bottleneck**: All 22 paths converge on `sendMessagesB_``withConnLocks` (Agent.hs:1708). The deadlock is between this lock acquisition and any worker thread holding `connLock` + blocking on `writeTBQueue subQ`.
2. **Highest-risk paths** (#1, #2): Broadcasting to ALL group members in `introduceToAll` / `introduceToRemaining` acquires `withConnLocks` on ALL member connIds in a single batch. For large groups, this holds the agentSubscriber thread for a long time, during which subQ fills, which causes worker threads holding connLock on any of those connIds to deadlock.
3. **Medium-risk paths** (#5-7): `sendPendingGroupMessages` fires on every CON/SENT/QCONT. These are frequent and lock the member's connId, which is the SAME connId that a delivery worker or ACK worker may hold while writing to subQ.
---
## Analysis: `withConnLocks` in `sendMessagesB_`
### FACT: the lock protects ratchet encryption state
`sendMessagesB_` (Agent.hs:1708-1713) acquires `withConnLocks` and executes:
1. **`getConn_`** — reads connection metadata, send queues from DB
2. **`setConnPQSupport`** — updates PQ encryption flag per connection
3. **`enqueueMessagesB`** → `enqueueMessageB``storeSentMsg_` which calls:
- **`updateSndIds`** (AgentStore.hs:899) — increments `internalSndId` (sequential send counter)
- **`agentRatchetEncryptHeader`** (Agent.hs:3698) — reads current ratchet via `getRatchetForUpdate`, encrypts message header via `rcEncryptHeader`, writes advanced ratchet state via `updateRatchet`
- **`createSndMsg`** + **`createSndMsgDelivery`** — inserts message and delivery records
All operations run within `unsafeWithStore``withTransaction` (single DB transaction per batch).
### FACT: the lock CANNOT be removed
Without `withConnLocks`, concurrent `sendMessagesB_` calls targeting the same connection would:
- Read the same ratchet state, both encrypt, one overwrite the other → **ratchet desync** (unrecoverable)
- Get duplicate `internalSndId` values → **message ID collision**
- Race on `setConnPQSupport` → **PQ state inconsistency**
The lock serializes ALL operations on the connection's encryption state. Removing it would introduce data corruption.
Note: `sendMessage` (singular, line 530) uses the same `sendMessagesB_` function — there is no lock-free send path.
### Eliminated strategies
- **Strategy C (remove lock)**: The lock protects ratchet encryption. Removing it causes unrecoverable ratchet desync. Eliminated.
- **Strategy A (async dispatch)**: All 22 chat-layer callers use `deliverMessagesB` return values (delivery IDs, PQ state) synchronously. `forkIO` loses results. Eliminated.
- **Strategy W (isFullTBQueue + pending TVar)**: The existing pattern (lines 1937, 3216) buffers events in a local TVar and flushes after lock release. Between lock release and flush, another thread can acquire the same connLock and write events to subQ — reordering events within the same connection. This trades a visible deadlock for invisible ordering bugs. Eliminated.
- **Strategy O (per-connection overflow queues)**: Bounded overflow queues with "drop when full" were analyzed. Drop consequences are unacceptable at 5 of 6 write sites — CONF, INFO, CON cause permanent connection failure after ACK; INV loses connection invitations; SENT/MERR leave messages stuck forever. Unbounded overflow defeats backpressure. Eliminated.
---
## Solution: move subQ writes outside connLock
### Root cause
The `writeTBQueue subQ` calls at the 3 deadlock sites are inside `connLock` by accident of code structure, not necessity. `connLock` protects ratchet encryption state and DB consistency. The `notify` calls write informational events to `subQ` — they do not modify any state that `connLock` protects.
Moving the writes outside the lock scope eliminates the deadlock: blocking `writeTBQueue subQ` without holding `connLock` is safe — agentSubscriber is free to acquire the lock, process events, and drain `subQ`.
### Why reordering doesn't matter at these sites
The chat layer handlers for the 3 deadlock site events do NOT advance the ratchet:
| Event | Chat handler | Calls sendMessagesB_? |
|-------|-------------|----------------------|
| SWITCH SPCompleted | Creates internal chat item, updates UI | **No** |
| ERR INTERNAL | Logs error to view | **No** |
| MSGNTF | `toView CEvtNtfMessage` → empty output | **No** |
Events that DO trigger ratchet advances (CON, SENT, QCONT → `sendPendingGroupMessages``sendMessagesB_`) are all already written OUTSIDE `connLock` in the current code.
Ratchet state lives in the DB, not in subQ events. agentSubscriber processes events sequentially regardless of arrival order. The SENT-before-SWITCH race already exists in the current code (new queue worker writes SENT outside connLock while old queue worker writes SWITCH inside connLock).
### Fix: Site 1 — `runSmpQueueMsgDelivery` AM_QTEST_ (line 2187)
Restructure `withConnLock` to return the event, write outside.
**Current code** (Agent.hs:2187-2214):
```haskell
AM_QTEST_ -> withConnLock c connId "runSmpQueueMsgDelivery AM_QTEST_" $ do
withStore' c $ \db -> setSndQueueStatus db sq Active
SomeConn _ conn <- withStore c (`getConn` connId)
case conn of
DuplexConnection cData' rqs sqs -> do
let addr = qAddress sq
case findQ addr sqs of
Just SndQueue {dbReplaceQueueId = Just replacedId, primary} ->
case removeQP (\sq' -> dbQId sq' == replacedId && not (sameQueue addr sq')) sqs of
Nothing -> internalErr msgId "sent QTEST: queue not found in connection"
Just (sq', sq'' : sqs') -> do
checkSQSwchStatus sq' SSSendingQTEST
atomically $ TM.delete (qAddress sq') $ smpDeliveryWorkers c
withStore' c $ \db -> do
when primary $ setSndQueuePrimary db connId sq
deletePendingMsgs db connId sq'
deleteConnSndQueue db connId sq'
let sqs'' = sq'' :| sqs'
conn' = DuplexConnection cData' rqs sqs''
cStats <- connectionStats c conn'
notify $ SWITCH QDSnd SPCompleted cStats -- DEADLOCK
_ -> internalErr msgId "sent QTEST: ..." -- DEADLOCK (via notifyDel → notify)
_ -> internalErr msgId "sent QTEST: ..." -- DEADLOCK
_ -> internalErr msgId "QTEST sent not in duplex ..." -- DEADLOCK
```
**New code:**
```haskell
AM_QTEST_ -> do
evt_ <- withConnLock c connId "runSmpQueueMsgDelivery AM_QTEST_" $ do
withStore' c $ \db -> setSndQueueStatus db sq Active
SomeConn _ conn <- withStore c (`getConn` connId)
case conn of
DuplexConnection cData' rqs sqs -> do
let addr = qAddress sq
case findQ addr sqs of
Just SndQueue {dbReplaceQueueId = Just replacedId, primary} ->
case removeQP (\sq' -> dbQId sq' == replacedId && not (sameQueue addr sq')) sqs of
Nothing -> pure $ Left "sent QTEST: queue not found in connection"
Just (sq', sq'' : sqs') -> do
checkSQSwchStatus sq' SSSendingQTEST
atomically $ TM.delete (qAddress sq') $ smpDeliveryWorkers c
withStore' c $ \db -> do
when primary $ setSndQueuePrimary db connId sq
deletePendingMsgs db connId sq'
deleteConnSndQueue db connId sq'
let sqs'' = sq'' :| sqs'
conn' = DuplexConnection cData' rqs sqs''
cStats <- connectionStats c conn'
pure $ Right $ SWITCH QDSnd SPCompleted cStats
_ -> pure $ Left "sent QTEST: there is only one queue in connection"
_ -> pure $ Left "sent QTEST: queue not in connection or not replacing another queue"
_ -> pure $ Left "QTEST sent not in duplex connection"
-- subQ write is now OUTSIDE connLock — blocking writeTBQueue is safe
case evt_ of
Right evt -> notify evt
Left err -> internalErr msgId err
```
All DB operations remain inside the lock. Only `notify`/`internalErr` (which write to subQ) move outside. `internalErr` calls `notifyDel` = `notify >> delMsg` — both `notify` (subQ write) and `delMsg` (`deleteSndMsgDelivery`, keyed on unique msgId) are safe outside the lock. The existing double-delete pattern (`delMsg` inside `internalErr` + `delMsgKeep` at line 2216) is preserved.
### Fix: Sites 2 & 3 — `ackQueueMessage::sendMsgNtf` (line 2386)
Change `ackQueueMessage` to return the MSGNTF event instead of writing it to subQ. Callers write to subQ after releasing connLock.
**Current code** (Agent.hs:2371-2386):
```haskell
ackQueueMessage :: AgentClient -> RcvQueue -> SMP.MsgId -> AM ()
ackQueueMessage c rq@RcvQueue {userId, connId, server} srvMsgId = do
atomically $ incSMPServerStat c userId server ackAttempts
tryAllErrors (sendAck c rq srvMsgId) >>= \case
Right _ -> sendMsgNtf ackMsgs
Left (SMP _ SMP.NO_MSG) -> sendMsgNtf ackNoMsgErrs
Left e -> ...
where
sendMsgNtf stat = do
atomically $ incSMPServerStat c userId server stat
whenM (liftIO $ hasGetLock c rq) $ do
atomically $ releaseGetLock c rq
brokerTs_ <- eitherToMaybe <$> tryAllErrors (withStore c $ \db -> getRcvMsgBrokerTs db connId srvMsgId)
atomically $ writeTBQueue (subQ c) ("", connId, AEvt SAEConn $ MSGNTF srvMsgId brokerTs_)
```
**New code** — return `Maybe ATransmission` instead of writing:
```haskell
ackQueueMessage :: AgentClient -> RcvQueue -> SMP.MsgId -> AM (Maybe ATransmission)
ackQueueMessage c rq@RcvQueue {userId, connId, server} srvMsgId = do
atomically $ incSMPServerStat c userId server ackAttempts
tryAllErrors (sendAck c rq srvMsgId) >>= \case
Right _ -> sendMsgNtf ackMsgs
Left (SMP _ SMP.NO_MSG) -> sendMsgNtf ackNoMsgErrs
Left e -> ... >> pure Nothing
where
sendMsgNtf stat = do
atomically $ incSMPServerStat c userId server stat
ifM (liftIO $ hasGetLock c rq)
(do atomically $ releaseGetLock c rq
brokerTs_ <- eitherToMaybe <$> tryAllErrors (withStore c $ \db -> getRcvMsgBrokerTs db connId srvMsgId)
pure $ Just ("", connId, AEvt SAEConn $ MSGNTF srvMsgId brokerTs_))
(pure Nothing)
```
**Caller 1: `ackMessage'`** (Agent.hs:2253-2267) — return event from `withConnLock`, write after:
```haskell
ackMessage' c connId msgId rcptInfo_ = do
t_ <- withConnLock c connId "ackMessage" $ do
SomeConn _ conn <- withStore c (`getConn` connId)
case conn of
DuplexConnection {} -> do
t_ <- ack
sendRcpt conn
del
pure t_
RcvConnection {} -> do
t_ <- ack
del
pure t_
SndConnection {} -> throwE $ CONN SIMPLEX "ackMessage"
ContactConnection {} -> throwE $ CMD PROHIBITED "ackMessage: ContactConnection"
NewConnection _ -> throwE $ CMD PROHIBITED "ackMessage: NewConnection"
-- subQ write is OUTSIDE connLock
case t_ of
Just t -> atomically $ writeTBQueue (subQ c) t
Nothing -> pure ()
```
**Caller 2: `ICAck` / `ICAckDel`** (Agent.hs:1823-1824) — inline `tryWithLock` as `tryCommand` + `withConnLock`, write subQ between the two scopes:
`tryWithLock name = tryCommand . withConnLock c connId name` — by inlining, the subQ write can be placed outside `withConnLock` but inside `tryCommand` (retaining retry/error handling).
```haskell
ICAck rId srvMsgId -> withServer $ \srv ->
tryCommand $ do
t_ <- withConnLock c connId "ICAck" $ ack srv rId srvMsgId
-- subQ write is OUTSIDE connLock — cannot deadlock with agentSubscriber
forM_ t_ $ atomically . writeTBQueue subQ
ICAckDel rId srvMsgId msgId -> withServer $ \srv ->
tryCommand $ do
t_ <- withConnLock c connId "ICAckDel" $ do
t_ <- ack srv rId srvMsgId
withStore' c (\db -> deleteMsg db connId msgId)
pure t_
-- subQ write is OUTSIDE connLock — cannot deadlock with agentSubscriber
forM_ t_ $ atomically . writeTBQueue subQ
```
Where `ack` now returns `AM (Maybe ATransmission)`:
```haskell
ack srv rId srvMsgId = do
rq <- withStore c $ \db -> getRcvQueue db connId srv rId
ackQueueMessage c rq srvMsgId
```
All subQ writes for MSGNTF are now outside connLock. FIFO ordering is preserved — no `nonBlockingWriteTBQueue`, no forked threads. The same thread that held the lock writes to subQ sequentially after releasing it.
### Race analysis
Window between connLock release and subQ write at Site 1:
| Thread | Can acquire connLock(X)? | Writes subQ? | Consequence |
|--------|-------------------------|-------------|-------------|
| agentSubscriber via sendMessagesB_ | Yes | **No** (encrypts only) | No race |
| processSMP for connId X | Yes | Yes (pending flush) | MSG before SWITCH — cosmetic |
| runCommandProcessing for connId X | Yes | Yes (pending flush) | Command response before SWITCH — cosmetic |
| New queue delivery worker | No (SENT outside lock) | Yes | SENT before SWITCH — cosmetic, **already exists in current code** |
All races are cosmetic UI ordering. None affect ratchet state, protocol correctness, or message delivery.
### Summary of changes
| File | Change | Lines affected |
|------|--------|---------------|
| Agent.hs | Restructure AM_QTEST_ to return event from `withConnLock`, write outside | ~2187-2214 |
| Agent.hs | Change `ackQueueMessage` return type to `AM (Maybe ATransmission)`, return event instead of writing | ~2371-2386 |
| Agent.hs | `ackMessage'`: return event from `withConnLock`, write outside | ~2253-2267 |
| Agent.hs | `ICAck`/`ICAckDel`: inline `tryCommand` + `withConnLock`, write subQ between scopes | ~1823-1824 |
| Agent.hs | `ack` helper: propagate new return type | ~1899-1901 |
No new data structures. No new modules. No changes to other write sites (1937, 3216 — already safe). ~25 lines changed total.
+734
View File
@@ -0,0 +1,734 @@
Version 7, 2025-01-24
# SMP agent protocol - duplex communication over SMP protocol
## Table of contents
- [Abstract](#abstract)
- [SMP agent](#smp-agent)
- [SMP routers management](#smp-routers-management)
- [SMP agent protocol scope](#smp-agent-protocol-scope)
- [Duplex connection procedure](#duplex-connection-procedure)
- [Fast duplex connection procedure](#fast-duplex-connection-procedure)
- [Contact addresses](#contact-addresses)
- [Communication between SMP agents](#communication-between-smp-agents)
- [Message syntax](#messages-between-smp-agents)
- [HELLO message](#hello-message)
- [A_MSG message](#a_msg-message)
- [A_RCVD message](#a_rcvd-message)
- [EREADY message](#eready-message)
- [A_QCONT message](#a_qcont-message)
- [Rotating messaging queue](#rotating-messaging-queue)
- [End-to-end encryption](#end-to-end-encryption)
- [Connection link: 1-time invitation and contact address](#connection-link-1-time-invitation-and-contact-address)
- [Full connection link syntax](#full-connection-link-syntax)
- [Short connection link syntax](#short-connection-link-syntax)
- [Short links](#short-links)
- [Link key derivation](#link-key-derivation)
- [Link data encryption](#link-data-encryption)
- [Short link resolution](#short-link-resolution)
- [Link data management](#link-data-management)
- [Appendix A: SMP agent API](#appendix-a-smp-agent-api)
- [API functions](#api-functions)
- [API events](#api-events)
## Abstract
The purpose of SMP agent protocol is to define the syntax and the semantics of communications between the client and the agent that connects to [SMP](./simplex-messaging.md) routers.
It provides:
- API to create and manage bi-directional (duplex) connections between the users of SMP agents consisting of two (or more) separate unidirectional (simplex) SMP queues, abstracting away multiple steps required to establish bi-directional connections and any information about the routers location from the users of the agent protocol.
- management of E2E encryption between SMP agents, generating ephemeral asymmetric keys for each connection.
- SMP command authentication on SMP routers, generating ephemeral keys for each SMP queue.
- TCP/TLS transport handshake with SMP routers.
- validation of message integrity.
SMP agent API provides no security between the agent and the client - it is assumed that the agent is executed in the trusted and secure environment, via the agent library, when the agent logic is included directly into the client application - [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat) uses this approach.
This document describes SMP agent protocol version 7. The version history:
- v1: initial version
- v2: duplex handshake - allows including reply queue(s) in the initial confirmation
- v3: ratchet sync - supports re-negotiating double ratchet encryption
- v4: delivery receipts - supports acknowledging message delivery to the sender
- v5: post-quantum - supports post-quantum key exchange in double ratchet (PQDR)
- v6: sender auth key - supports sender authentication key in confirmations
- v7: ratchet on confirmation - initializes double ratchet during confirmation
## SMP agent
SMP agents communicate with each other via SMP routers using [simplex messaging protocol (SMP)](./simplex-messaging.md) according to the API calls used by the client applications. This protocol is a middle layer in SimpleX protocols (above SMP protocol but below any application level protocol) - it is intended to be used by client-side applications that need secure asynchronous bi-directional communication channels ("connections").
The agent must have a persistent storage to manage the states of known connections and of the client-side information of SMP queues that each connection consists of, and also the buffer of the most recent sent and received messages. The number of the messages that should be stored is implementation specific, depending on the error management approach that the agent implements; at the very least the agent must store the hashes and IDs of the last received and sent messages.
## SMP routers management
SMP agent API does not use the addresses of the SMP routers that the agent will use to create and use the connections (excluding the router address in queue URIs used in JOIN command). The list of the routers is a part of the agent configuration and can be dynamically changed by the agent implementation:
- by the client applications via any API that is outside of scope of this protocol.
- by the agents themselves based on availability and latency of the configured routers.
## SMP agent protocol scope
SMP agent protocol has 2 main parts:
- the messages that SMP agents exchange with each other in order to:
- negotiate establishing unidirectional (simplex) encrypted queues on SMP routers.
- exchange client messages and delivery notifications, providing sequential message IDs and message integrity (by including the hash of the previous message).
- re-negotiate messaging queues to use and connection e2e encryption.
- the messages that the clients of SMP agents should send out-of-band (as pre-shared "invitation" including queue URIs) to protect [E2E encryption][1] from active attacks ([MITM attacks][2]).
[Appendix A](#appendix-a-smp-agent-api) of this document describes:
- the functional API used by the client application with the agent. This API allows to create and manage multiple connections, each consisting of two or more SMP queues.
- events that the agent passes to the clients.
## Duplex connection procedure
![Duplex connection procedure](./diagrams/duplex-messaging/duplex-creating.svg)
The procedure of establishing a duplex connection is explained on the example of Alice and Bob creating a bi-directional connection consisting of two unidirectional (simplex) queues, using SMP agents (A and B) to facilitate it, and two different SMP routers (which could be the same router). It is shown on the diagram above and has these steps:
1. Alice requests the new connection from the SMP agent A using agent `createConnection` api function.
2. Agent A creates an SMP queue on the router (using [SMP protocol](./simplex-messaging.md) `NEW` command) and responds to Alice with the invitation that contains queue information and the encryption keys Bob's agent B should use. The invitation format is described in [Connection link](connection-link-1-time-invitation-and-contact-address).
3. Alice sends the [connection link](#connection-link-1-time-invitation-and-contact-address) to Bob via any secure channel (out-of-band message) - as a link or as a QR code.
4. Bob uses agent `joinConnection` api function with the connection link as a parameter to agent B to accept the connection.
5. Agent B creates Bob's SMP reply queue with SMP router `NEW` command.
6. Agent B confirms the connection: sends an "SMP confirmation" with SMP router `SEND` command to the SMP queue specified in the connection link - SMP confirmation is an unauthenticated message with an ephemeral key that will be used to authenticate Bob's commands to the queue, as described in SMP protocol, and Bob's info (profile, public key for E2E encryption, and the connection link to this 2nd queue to Agent A - this connection link SHOULD use "simplex" URI scheme). This message is encrypted using key passed in the connection link (or with the derived shared secret, in which case public key for key derivation should be sent in clear text).
7. Alice confirms and continues the connection:
- Agent A receives the SMP confirmation containing Bob's key, reply queue and info as SMP router `MSG`.
- Agent A notifies Alice sending `CONF` notification with Bob's info.
- Alice allows connection to continue with agent `allowConnection` api function.
- Agent A secures the queue with SMP router `KEY` command.
- Agent A sends SMP confirmation with ephemeral sender key, ephemeral public encryption key and profile (but without reply queue).
8. Agent B confirms the connection:
- receives the confirmation.
- sends the notification `INFO` with Alice's information to Bob.
- secures SMP queue that it sent to Alice in the first confirmation with SMP `KEY` command .
- sends `HELLO` message via SMP `SEND` command. This confirms that the reply queue is secured and also validates that Agent A secured the first SMP queue
9. Agent A notifies Alice.
- receives `HELLO` message from Agent B.
- sends `HELLO` message to Agent B via SMP `SEND` command.
- sends `CON` notification to Alice, confirming that the connection is established.
10. Agent B notifies Bob.
- Once Agent B receives `HELLO` from Agent A, it sends to Bob `CON` notification as well.
At this point the duplex connection between Alice and Bob is established, they can use `SEND` command to send messages. The diagram also shows how the connection status changes for both parties, where the first part is the status of the SMP queue to receive messages, and the second part - the status of the queue to send messages.
The most communication happens between the agents and routers, from the point of view of Alice and Bob there are 4 steps (not including notifications):
1. Alice requests a new connection with `createConnection` agent API function and receives the connection link.
2. Alice passes connection link out-of-band to Bob.
3. Bob accepts the connection with `joinConnection` agent API function with the connection link to his agent.
4. Alice accepts the connection with `allowConnection` agent API function.
5. Both parties receive `CON` notification once duplex connection is established.
Clients SHOULD support establishing duplex connection asynchronously (when parties are intermittently offline) by persisting intermediate states and resuming SMP queue subscriptions.
## Fast duplex connection procedure
Previously described duplex connection procedure requires sending 4 messages creating a bad UX for the users - it requires waiting until each party in online before the messages can be sent.
It allows users validating connecting party profile before proceeding with the connection, but it turned out to be unnecessary UX step and is not used in the client applications.
It also protects against an attacker who compromised TLS and uses the sender queue ID sent to the recipient to secure the queue before the sender can. This attack is very hard, and this accepting its risk is better than worse UX. Future protocol versions could mitigate this attack by encrypting entity IDs.
Faster duplex connection process is possible with the `SKEY` command added in v9 of SMP protocol.
![Fast duplex connection procedure](./diagrams/duplex-messaging/duplex-creating-fast.svg)
1. Alice requests the new connection from the SMP agent A using agent `createConnection` api function
2. Agent A creates an SMP queue on the router (using [SMP protocol](./simplex-messaging.md) `NEW` command with the flag allowing the sender to secure the queue) and responds to Alice with the invitation that contains queue information and the encryption keys Bob's agent B should use. The invitation format is described in [Connection link](connection-link-1-time-invitation-and-contact-address).
3. Alice sends the [connection link](connection-link-1-time-invitation-and-contact-address) to Bob via any secure channel (out-of-band message) - as a link or as a QR code. This link contains the flag that the queue can be secured by the sender.
4. Bob uses agent `joinConnection` api function with the connection link as a parameter to agent B to accept the connection.
5. Agent B secures Alice's queue with SMP command `SKEY` - this command can be proxied.
6. Agent B creates Bob's SMP reply queue with SMP router `NEW` command (with the flag allowing the sender to secure the queue).
7. Agent B confirms the connection: sends an "SMP confirmation" with SMP router `SEND` command to the SMP queue specified in the connection link - SMP confirmation is an unauthenticated message with an ephemeral key that will be used to authenticate Bob's commands to the queue, as described in SMP protocol, and Bob's info (profile, public key for E2E encryption, and the connection link to this 2nd queue to Agent A - this connection link SHOULD use "simplex" URI scheme). This message is encrypted using key passed in the connection link (or with the derived shared secret, in which case public key for key derivation should be sent in clear text).
8. Alice confirms the connection:
- Agent A receives the SMP confirmation containing Bob's key, reply queue and info as SMP router `MSG`.
- Agent A notifies Alice sending `CONF` notification with Bob's info (that indicates that Agent B already secured the queue).
- Alice allows connection to continue with agent `allowConnection` api function.
- Agent A secures Bob's queue with SMP command `SKEY`.
- Agent A sends SMP confirmation with ephemeral public encryption key and profile (but without reply queue, and without sender key).
9. Agent A notifies Alice with `CON` notification.
10. Agent B notifies Bob about connection success:
- receives confirmation message from Alice.
- sends the notification `INFO` with Alice's information to Bob.
- sends `CON` notification to Bob.
## Contact addresses
SMP agents support creating a special type of connection - a contact address - that allows to connect to multiple network users who can send connection requests by sending 1-time connection links to the message queue.
This connection address uses a messaging queue on SMP router to receive invitations to connect - see `agentInvitation` message below. Once connection request is accepted, a new connection is created and the address itself is no longer used to send the messages - deleting this address does not disrupt the connections that were created via it.
## Communication between SMP agents
To establish duplex connections and to send messages on behalf of their clients, SMP agents communicate via SMP routers.
Agents use SMP message client body (the part of the SMP message after header - see [SMP protocol](./simplex-messaging.md)) to transmit agent client messages and exchange messages between each other.
These messages are encrypted with per-queue shared secret using NaCL crypto_box and can be of 4 types, as defined by `decryptedSMPClientMessage`:
- `agentConfirmation` - used when confirming SMP queues, contains connection information encrypted with double ratchet. This envelope can only contain `agentConnInfo` or `agentConnInfoReply` encrypted with double ratchet.
- `agentMsgEnvelope` - contains different agent messages encrypted with double ratchet, as defined in `agentMessage`.
- `agentInvitation` - sent to SMP queue that is used as contact address, does not use double ratchet.
- `agentRatchetKey` - used to re-negotiate double ratchet encryption - can contain additional information in `agentRatchetInfo`.
```abnf
decryptedSMPClientMessage = agentConfirmation / agentMsgEnvelope / agentInvitation / agentRatchetKey
agentConfirmation = agentVersion %s"C" ("0" / "1" sndE2EEncryptionParams) encConnInfo
agentVersion = 2*2 OCTET
sndE2EEncryptionParams = <sender E2E ratchet parameters, see pqdr.md>
encConnInfo = doubleRatchetEncryptedMessage
agentMsgEnvelope = agentVersion %s"M" encAgentMessage
encAgentMessage = doubleRatchetEncryptedMessage
agentInvitation = agentVersion %s"I" connReqLength connReq connInfo
connReqLength = 2*2 OCTET ; Word16
connReq = *OCTET ; URI text encoding of connection link, length given by connReqLength
connInfo = *OCTET ; opaque connection information (remaining bytes)
agentRatchetKey = agentVersion %s"R" rcvE2EEncryptionParams ratchetKeyInfo
rcvE2EEncryptionParams = <receiver E2E ratchet parameters, see pqdr.md>
ratchetKeyInfo = *OCTET ; additional ratchet renegotiation info (remaining bytes)
doubleRatchetEncryptedMessage = <double ratchet encrypted message, see pqdr.md>
```
The maximum size of the encrypted connection info and agent message depend on whether post-quantum key exchange is used:
| Constant | PQ on | PQ off |
|----------|-------|--------|
| `e2eEncConnInfoLength` | 11106 | 14832 |
| `e2eEncAgentMsgLength` | 13618 | 15840 |
The PQ-on sizes are smaller because the ratchet header and reply link include larger PQ keys (SNTRUP761).
This syntax of decrypted SMP client message body is defined by `decryptedAgentMessage` below.
Decrypted SMP message client body can be one of 4 types:
- `agentConnInfo` - used by the initiating party when confirming reply queue - sent in `agentConfirmation` envelope.
- `agentConnInfoReply` - used by accepting party, includes reply queue(s) in the initial confirmation - sent in `agentConfirmation` envelope.
- `agentRatchetInfo` - used to pass additional information when renegotiating double ratchet encryption - sent in `agentRatchetKey` envelope.
- `agentMessage` - all other agent messages.
`agentMessage` contains these parts:
- `agentMsgHeader` - agent message header that contains sequential agent message ID for a particular SMP queue and the hash of the previous message.
- `aMessage` - a command/message to the other SMP agent:
- to confirm the connection (`HELLO`).
- to send and to confirm reception of user messages (`A_MSG`, `A_RCVD`).
- to confirm that the new double ratchet encryption is agreed (`EREADY`).
- to notify another party that it can continue sending messages after queue capacity was exceeded (`A_QCONT`).
- to manage SMP queue rotation (`QADD`, `QKEY`, `QUSE`, `QTEST`).
The encoded `agentMessage` is padded to a fixed size by the double ratchet encryption layer (see [ratchet message wire format](./pqdr.md#ratchet-message-wire-format)) to make all SMP messages have constant size, preventing routers from observing the actual message size.
### Messages between SMP agents
Message syntax below uses [ABNF][3] with [case-sensitive strings extension][4].
```abnf
decryptedAgentMessage = agentConnInfo / agentConnInfoReply / agentRatchetInfo / agentMessage
agentConnInfo = %s"I" connInfo
connInfo = *OCTET
agentConnInfoReply = %s"D" smpQueues connInfo
smpQueues = length 1*newQueueInfo ; NonEmpty list of reply queues
agentRatchetInfo = %s"R" ratchetInfo
ratchetInfo = *OCTET
agentMessage = %s"M" agentMsgHeader aMessage
agentMsgHeader = agentMsgId prevMsgHash
agentMsgId = 8*8 OCTET ; Int64
prevMsgHash = shortString
aMessage = HELLO / A_MSG / A_RCVD / EREADY / A_QCONT /
QADD / QKEY / QUSE / QTEST
HELLO = %s"H"
A_MSG = %s"M" userMsgBody
userMsgBody = *OCTET ; remaining bytes
A_RCVD = %s"V" msgReceipts
msgReceipts = length 1*msgReceipt ; NonEmpty list
msgReceipt = agentMsgId msgHash rcptLength rcptInfo
msgHash = shortString
rcptInfo = *OCTET ; opaque receipt info, length given by rcptLength (Word16)
EREADY = %s"E" agentMsgId
A_QCONT = %s"QC" sndQueueAddr
QADD = %s"QA" sndQueues
sndQueues = length 1*(newQueueUri replacedSndQueue)
newQueueUri = clientVRange smpRouter senderId dhPublicKey [queueMode]
dhPublicKey = length x509encoded
queueMode = %s"M" / %s"C" ; M - messaging (sender can secure), C - contact
replacedSndQueue = "0" / "1" sndQueueAddr
QKEY = %s"QK" sndQueueKeys
sndQueueKeys = length 1*(newQueueInfo senderKey)
newQueueInfo = version smpRouter senderId dhPublicKey [queueMode]
senderKey = length x509encoded
QUSE = %s"QU" sndQueuesReady
sndQueuesReady = length 1*(sndQueueAddr primary)
primary = %s"T" / %s"F"
QTEST = %s"QT" sndQueueAddrs
sndQueueAddrs = length 1*sndQueueAddr
sndQueueAddr = smpRouter senderId
smpRouter = hosts port keyHash
hosts = length 1*host
host = shortString
port = shortString
keyHash = shortString
senderId = shortString
clientVRange = version version
version = 2*2 OCTET
rcptLength = 2*2 OCTET
shortString = length *OCTET
length = 1*1 OCTET
```
#### HELLO message
This is the first message that both agents send after the respective SMP queue is secured by the receiving agent (see diagram).
This message is not used with [fast duplex connection](#fast-duplex-connection-procedure).
#### A_MSG message
This is the agent envelope used to send client messages once the connection is established. This is different from the MSG sent by SMP router to the agent and MSG event from SMP agent to the client that are sent in different contexts.
#### A_RCVD message
This message is sent to confirm the client message reception. It includes a list of message receipts, each containing the received message number, message hash and receipt info.
#### EREADY message
This message is sent after re-negotiating a new double ratchet encryption with `agentRatchetKey`.
#### A_QCONT message
This message is sent to notify the sender client that it can continue sending the messages after queue capacity was exhausted.
### Rotating messaging queue
SMP agents SHOULD support 4 messages to rotate message reception to another messaging router:
`QADD`: add the new queue address(es) to the connection - sent by the client that initiates rotation.
`QKEY`: pass sender's key via existing connection (SMP confirmation message will not be used, to avoid the same "race" of the initial key exchange that would create the risk of intercepting the queue for the attacker) - sent by the client accepting the rotation
`QUSE`: instruct the sender to use the new queue with sender's queue ID as parameter. From this point some messages can be sent to both the new queue and the old queue.
`QTEST`: send test message to the new connection. Any other message can be sent if available to continue rotation, the absence of this message is not an error. Once this message is successfully sent the sender will stop using the old queue. Once this message (or any other message in the new queue) is received, the recipient will stop using the old queue and delete it.
**Queue rotation procedure**
![Queue rotation procedure](./diagrams/duplex-messaging/queue-rotation.svg)
`SKEY` command added in v9 of SMP protocol allows for faster queue rotation procedure.
**Fast queue rotation procedure**
![Fast queue rotation procedure](./diagrams/duplex-messaging/queue-rotation-fast.svg)
## End-to-end encryption
Messages between SMP agents have two layers of e2e encryption:
- simple encryption agreed in SMP protocol with a fixed key agreed when the messaging queue is agreed by parties.
- post-quantum resistant augmented double ratchet algorithm (PQDR) specified in [this document](./pqdr.md).
The protocol supports adding and removing post-quantum KEM primitive to the key agreement in double ratchet:
- to support migration of pre-existing connections to PQDR.
- to be able to disable PQ key agreement.
- to be able to use invitation links and contact addresses without large PQ keys.
Possible scenarios below show the possible states of PQ key agreement, assuming that both clients support it.
Possible options for each stage are:
- no KEM encapsulation key was sent (No PQ key),
- only KEM encapsulation key was sent, but not ciphertext yet (PQ key sent),
- both KEM encapsulation key from one KEM agreement and ciphertext from the previous agreement were sent (PQ key + PQ ct sent).
`+` in the table means that this scenario is possible, and `-` - that it is not possible.
| Connection stage | No PQ key | PQ key sent | PQ key + PQ ct sent |
|:------------------------------------------------------:|:----------------:|:----------------:|:-------------------:|
| invitation | + | + | - |
| confirmation, in reply to: <br>no-pq inv <br>pq inv | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |
| 1st msg, in reply to: <br>no-pq conf <br>pq/pq+ct conf | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |
| Nth msg, in reply to: <br>no-pq msg <br>pq/pq+ct msg | &nbsp;<br>+<br>+ | &nbsp;<br>+<br>- | &nbsp;<br>-<br>+ |
These scenarios can be reduced to:
1. initial invitation optionally has PQ key, but must not have ciphertext.
2. all subsequent messages should be allowed without PQ key/ciphertext, but:
- if the previous message had PQ key or PQ key with ciphertext, they must either have no PQ key, or have PQ key with ciphertext (PQ key without ciphertext is an error).
- if the previous message had no PQ key, they must either have no PQ key, or have PQ key without ciphertext (PQ key with ciphertext is an error).
The rules for calculating the shared secret for received/sent messages are (assuming received message is valid according to the above rules):
| sent msg > <br>V received msg | no-pq | pq | pq+ct |
|:------------------------------:|:-----------:|:-------:|:---------------:|
| no-pq | DH / DH | DH / DH | err |
| pq (sent msg was NOT pq) | DH / DH | err | DH / DH+KEM |
| pq+ct (sent msg was NOT no-pq) | DH+KEM / DH | err | DH+KEM / DH+KEM |
To summarize, the upgrade to DH+KEM secret happens in a sent message that has PQ key with ciphertext sent in reply to message with PQ key only (without ciphertext), and the downgrade to DH secret happens in the message that has no PQ key.
## Connection link: 1-time invitation and contact address
Connection links are generated by SMP agent in response to `createConnection` api call, used by another party user with `joinConnection` api, and then another connection link is sent by the agent in `agentConnInfoReply` and used by the first party agent to connect to the reply queue (the second part of the process is invisible to the users).
### Full connection link syntax
```
connectionLink = connectionScheme "/" connLinkType "#/?v=" versionRange "&smp=" smpQueues ["&e2e=" e2eEncryption] ["&data=" clientData]
connLinkType = %s"invitation" / %s"contact"
connectionScheme = (%s"https://" clientAppServer) / %s"simplex:"
clientAppServer = hostname [ ":" port ]
; client app server, e.g. simplex.chat
versionRange = 1*DIGIT / 1*DIGIT "-" 1*DIGIT ; agent version range
e2eEncryption = <e2e encryption parameters for double ratchet>
smpQueues = smpQueue *(";" smpQueue) ; SMP queues for the connection (semicolon-separated)
smpQueue = <URL-encoded queueURI defined in SMP protocol>
clientData = <URL-encoded application-specific data>
```
All parameters are passed via URI hash to avoid sending them to the router (in case "https" scheme is used) - they can be used by the client-side code and processed by the client application. Parameters can be present in any order, any unknown additional parameters SHOULD be ignored.
`clientAppServer` is not an SMP router - it is a server that shows the instruction on how to download the client app that will connect using this connection link. This server can also host a mobile or desktop app manifest so that this link is opened directly in the app if it is installed on the device.
"simplex" URI scheme in `connectionProtocol` can be used instead of client app router, to connect without creating any web traffic. Client apps MUST support this URI scheme.
See SMP protocol [out-of-band messages](./simplex-messaging.md#out-of-band-messages) for syntax of `queueURI`.
### Short connection link syntax
Short links provide a more compact representation by storing connection data on the router:
```
shortLink = shortLinkScheme "/" linkType "#" [linkId "/"] linkKey ["?" shortLinkParams]
shortLinkScheme = %s"simplex:" / (%s"https://" serverHost)
linkType = %s"i" / contactType ; i - invitation, or contact type
contactType = %s"a" / %s"c" / %s"g" / %s"r" ; a - contact, c - channel, g - group, r - relay
linkId = base64url ; only for invitation links
linkKey = base64url ; SHA3-256 hash of fixed data, used to decrypt link data
shortLinkParams = hostParam ["&" portParam] ["&" keyHashParam]
hostParam = %s"h=" hostList
hostList = host *("," host)
portParam = %s"p=" port
keyHashParam = %s"c=" base64url ; router certificate fingerprint
```
Contact types:
- `a` (CCTContact) - direct contact connection
- `c` (CCTChannel) - channel connection
- `g` (CCTGroup) - group connection
- `r` (CCTRelay) - relay connection
Short links can use either the `simplex:` scheme or `https://` with a router hostname. When using the simplex scheme, router information is included in query parameters.
## Short links
Short links provide a compact representation of connection links by storing encrypted connection data on the SMP router. The link key in the URI fragment (after `#`) is never sent to the router, ensuring the router cannot decrypt the stored connection data.
### Link key derivation
The link key is derived from the fixed link data using SHA3-256 hash function:
```
linkKey = SHA3-256(fixedLinkData)
```
The fixed link data includes:
- Agent version range
- Root public key (Ed25519) for signing
- SMP queue connection request (router, queue IDs, encryption keys)
- Optional link entity ID
For contact links, the link ID and encryption key are derived from the link key using HKDF:
```
(linkId, encryptionKey) = HKDF(info="SimpleXContactLink", key=linkKey, outputLen=56)
; linkId = first 24 bytes, encryptionKey = remaining 32 bytes
```
For invitation links, the link ID is stored separately (usually included in the URI), and only the encryption key is derived:
```
encryptionKey = HKDF(info="SimpleXInvLink", key=linkKey, outputLen=32)
```
### Link data encryption
Link data stored on the router consists of two encrypted parts: fixed data and user data. Both are encrypted using NaCl secret_box (XSalsa20-Poly1305) with the derived encryption key:
```abnf
queueLinkData = encFixedData encUserData
encFixedData = largeString ; encrypted padded(signedFixedData, 2008)
encUserData = largeString ; encrypted padded(signedUserData, 13784)
signedFixedData = signature fixedData
signedUserData = signature userData
signature = length 64*64 OCTET ; Ed25519 signature
fixedData = agentVersionRange rootKey linkConnReq [linkEntityId]
agentVersionRange = version version ; min and max agent protocol version
version = 2*2 OCTET
rootKey = length x509encoded ; Ed25519 public key
linkConnReq = invitationConnReq / contactConnReq ; binary encoding of connection request
invitationConnReq = %s"I" connReqData e2eRatchetParams
contactConnReq = %s"C" connReqData
linkEntityId = shortString
userData = invitationLinkData / contactLinkData
invitationLinkData = %s"I" agentVersionRange userLinkData
contactLinkData = %s"C" agentVersionRange userContactData
userLinkData = shortString / (%xFF largeString) ; opaque application data (e.g., user profile)
; shortString length byte 0x00-0xFE (max 254 bytes); 0xFF is reserved as largeString sentinel
userContactData = direct ownersList relaysList userLinkData
direct = %s"T" / %s"F" ; whether direct connection via connReq is allowed
ownersList = length *ownerAuth
ownerAuth = shortString ; length-prefixed encoding of (ownerId ownerKey authOwnerSig)
ownerId = shortString ; application-specific owner ID (e.g., MemberId)
ownerKey = length x509encoded ; Ed25519 public key
authOwnerSig = length 64*64 OCTET ; Ed25519 signature of (ownerId || ownerKey) by previous owner
relaysList = length *connShortLink ; alternative relay short links
; Binary encoding of connection request (used in linkConnReq)
connReqData = agentVersionRange smpQueueUris clientData
smpQueueUris = length 1*smpQueueUri
clientData = %s"0" / (%s"1" largeString) ; Maybe (Large ByteString)
smpQueueUri = smpClientVersionRange smpServer senderId smpDhPublicKey [queueMode]
smpClientVersionRange = version version ; min and max SMP client versions
smpServer = hosts port serverKeyHash
hosts = length 1*host
host = shortString ; text-encoded hostname or IP address
port = shortString ; text-encoded port number
serverKeyHash = shortString ; CA certificate fingerprint
senderId = shortString ; queue sender ID
smpDhPublicKey = length x509encoded ; X25519 DH public key
queueMode = %s"M" / %s"C" ; messaging or contact (version-dependent trailing field)
e2eRatchetParams = e2eVersionRange e2eDhKey e2eDhKey kemParams
e2eVersionRange = version version ; min and max e2e encryption versions
e2eDhKey = length x509encoded ; X448 DH public key
kemParams = %s"0" / (%s"1" ratchetKEMParams)
ratchetKEMParams = %s"P" kemPublicKey / %s"A" kemCiphertext kemPublicKey
kemPublicKey = largeString ; sntrup761 public key
kemCiphertext = largeString ; sntrup761 ciphertext
; Binary encoding of short link (used in relaysList)
connShortLink = invShortLink / contactShortLink
invShortLink = %s"I" smpServer linkId linkKey
contactShortLink = %s"C" contactConnType smpServer linkKey
contactConnType = %s"A" / %s"C" / %s"G" / %s"R" ; contact / channel / group / relay
linkId = shortString
linkKey = shortString
x509encoded = *OCTET ; DER-encoded X.509 SubjectPublicKeyInfo
largeString = 2*2 OCTET *OCTET ; Word16 length prefix
length = 1*1 OCTET
shortString = length *OCTET
```
The fixed data is signed with the root key and its hash becomes the link key. The user data is signed either with the root key (for invitations) or with an owner key (for contact addresses).
### Short link resolution
When a user receives a short link, the agent resolves it as follows:
1. Extract the link key from the URI fragment
2. Send `LGET` command to the SMP router with the link ID
3. Receive encrypted link data from the router
4. Decrypt the link data using the link key
5. Extract the full connection information (SMP queue URI, encryption keys, profile)
6. Proceed with the standard connection procedure using `joinConnection`
For invitation links, the `LKEY` command is used to set the sender key when getting link data. Repeated `LKEY` would require using the same key.
### Link data management
The recipient who created the queue can manage the short link data:
- **LSET** - Set or update the link data associated with a queue. This is used when creating a short link or updating the user data (e.g., profile changes).
- **LDEL** - Delete the link data from the router. This effectively invalidates the short link.
Short links support different connection modes:
- **invitation** - One-time invitation links that can only be used once
- **contact** - Reusable contact address links that can be used multiple times
For contact addresses, the link data includes additional information about the contact type:
- **contact** - Direct contact connection
- **channel** - Channel connection
- **group** - Group connection
- **relay** - Relay connection
The agent maintains the link data and updates it when connection parameters change, ensuring short links remain valid and reflect current connection information.
## Appendix A: SMP agent API
The exact specification of agent library API and of the events that the agent sends to the client application is out of scope of the protocol specification.
The list of some of the API functions and events below is supported by the reference implementation, and they are likely to be required by the client applications.
### API functions
The list of APIs below is not exhaustive and provided for information only. Please consult the source code for more information.
#### Create connection
`createConnection` api is used to create a connection - it returns the connection link that should be sent out-of-band to another protocol user (the joining party). It should be used by the client of the agent that initiates creating a duplex connection (the initiating party).
This api is also used to create a contact address - a special connection that can be used by multiple people to connect to the user.
Some communication scenarios may require fault-tolerant mechanism of creating connections that retries on network failures and continue retrying after the client is restarted. Such asynchronous API would return its result via `INV` event once it succeeds.
#### Join connection
`joinConnection` is used to create a connection record and accept the connection invitation received out-of-band. It should be used by the client of the agent that accepts the connection (the joining party).
This api can also be required as asynchronous, in which case `OK` event will be dispatched to the client to indicate the success or `ERR` in case it permanently failed (e.g., in case connection was deleted by another party).
#### Allow connection
Once the client receives `CONF` event, it should use synchronous `allowConnection` api to proceed with the connection (both for the [standard](#duplex-connection-procedure) and for the [fast duplex procedure](#fast-duplex-connection-procedure)).
In case this API is used as asynchronous it will return its result via `OK` or `ERR` event.
#### Accept and reject connection requests
Connection requests are delivered to the client application via `REQ` event.
Client can `acceptContact` and `rejectContact`, with `OK` and `ERR` events in case of asynchronous calls.
#### Send message
`sendMessage` api is always asynchronous. The api call returns message ID, `SENT` event once the message is sent to the router, `MWARN` event in case of temporary delivery failure that can be resolved by the user (e.g., by connecting via Tor or by upgrading the client) and `MERR` in case of permanent delivery failure.
#### Acknowledge received message
Messages are delivered to the client application via `MSG` event.
Client application must always `ackMessage` to receive the next one - failure to call it in reference implementation will prevent the delivery of subsequent messages until the client reconnects to the router.
This api is also used to acknowledge message delivery to the sending party - that party client application will receive `RCVD` event.
#### Subscribe connection
`subscribeConnection` api is used by the client to resume receiving messages from the connection that was created in another TCP/client session.
#### Get notification message
`getNotificationMessage` is used by push notification subsystem of the client application to receive the message from a specific messaging queue mentioned in the notification. The client application would receive `MSG` and any other events from the agent, and then `MSGNTF` event once the message related to this notification is received.
#### Set short link data
`setConnectionLink` api (`LSET` command) is used to set or update short link data associated with a contact address queue. Returns `LINK` event with the short link URI.
#### Get short link data
`getConnectionLink` api (`LGET` command) is used to retrieve and decrypt the short link data from the router. Returns `LDATA` event with the decrypted link data.
#### Rotate message queue to another router
`switchConnection` api is used to rotate connection queues to another messaging router.
#### Renegotiate e2e encryption
`synchronizeRatchet` api is used to re-negotiate double ratchet encryption for the connection.
#### Delete connection
`deleteConnection` api is used to delete connection. In case of asynchronous call, the connection deletion will be confirmed with `DEL_RCVQS` and `DEL_CONNS` events.
#### Suspend connection
`suspendConnection` api is used to prevent any further messages delivered to the connection without deleting it.
### API events
Agent API uses these events dispatch to notify client application about events related to the connections:
- `INV` - connection invitation or connection address URI after connection is created.
- `CONF` - confirmation that connection is accepted by another party. When the accepting party uses `joinConnection` api to accept connection invitation, the initiating party will receive `CONF` notification with some identifier and additional information from the accepting party (e.g., profile). To continue the connection the initiating party client should use `allowConnection` api.
- `REQ` - connection request is sent when another party uses `joinConnection` api with contact address. The client application can use `acceptContact` or `rejectContact` api.
- `INFO` - information from the party that initiated the connection with `createConnection` sent to the party accepting the connection with `joinConnection`.
- `CON` - notification that connection is established sent to both parties of the connection.
- `END` - notification that connection subscription is terminated when another client subscribed to the same messaging queue.
- `DOWN` - notification that connection router is temporarily unavailable.
- `UP` - notification that the subscriptions made in the current client session are resumed after the router became available.
- `SWITCH` - notification about queue rotation process.
- `RSYNC` - notification about e2e encryption re-negotiation process.
- `SENT` - notification to confirm that the message was delivered to at least one of SMP routers. This notification contains the same message ID as returned to `sendMessage` api. `SENT` notification, depending on network availability, can be sent at any time later, potentially in the next client session.
- `MWARN` - temporary delivery failure that can be resolved by the user (e.g., by connecting via Tor or by upgrading the client).
- `MERR` - notification about permanent message delivery failure.
- `MERRS` - notification about permanent message delivery failure for multiple messages (e.g., when multiple messages expire).
- `MSG` - sent when agent receives the message from the SMP router.
- `MSGNTF` - sent after agent received and processed the message referenced in the push notification.
- `RCVD` - notification confirming message receipt by another party.
- `QCONT` - notification that the agent continued sending messages after queue capacity was exceeded and recipient received all messages.
- `LINK` - short link URI created or updated for a contact address.
- `LDATA` - decrypted short link data received from the router.
- `DELD` - notification that the connection was deleted.
- `JOINED` - notification that a member joined via a contact address.
- `STAT` - connection statistics event.
- `DEL_RCVQS` - confirmation that receiver message queues were deleted.
- `DEL_CONNS` - confirmation that connections were deleted.
- `OK` - confirmation that asynchronous api call was successful.
- `ERR` - error of asynchronous api call or some other error event.
This list of events is not exhaustive and provided for information only. Please consult the source code for more information.
## Threat model
This threat model complements SimpleX Messaging Protocol [threat model](./security.md#threat-model) with agent-level concerns: duplex connections, end-to-end encryption with [post-quantum double ratchet](./pqdr.md), message integrity, connection establishment and queue rotation. Only additional properties not covered in the SMP threat model are listed below.
#### Additional global assumptions
- The connection link is shared via a trusted out-of-band channel.
- Both agents support post-quantum double ratchet (PQDR).
#### A passive adversary
*cannot:*
- learn the contents of packets, which are additionally encrypted with the double ratchet independently from per-queue encryption.
#### Destination router (chosen by the receiving client application)
*can:*
- correlate queues belonging to the same duplex connection when queue rotation creates a new queue on the same router.
- when both peers of a connection chose the same router, correlate the two directions of the duplex connection.
*cannot:*
- compromise end-to-end encryption even with full access to the per-queue NaCl DH secret.
- correlate queues belonging to the same connection after queue rotation to a different router.
#### An attacker who obtained a client application's (decrypted) database
*can:*
- learn the full communication graph: all communication peers, associated router addresses, and queue identifiers.
*cannot:*
- decrypt future messages once the client application resumes communication and the double ratchet completes a new ratchet step, provided PQDR is active.
#### A communication peer
*can:*
- send malformed agent messages that may affect the client application processing them.
- skip message IDs, causing the recipient to generate and store excessive intermediate ratchet keys.
- prevent double ratchet advancement by not sending messages, delaying break-in recovery.
*cannot:*
- disrupt packet delivery in other queues.
#### An attacker who obtained a connection link
*can:*
- learn the initiating party's chosen router address and public keys.
*cannot:*
- use the link after the intended recipient has completed the connection.
[1]: https://en.wikipedia.org/wiki/End-to-end_encryption
[2]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack
[3]: https://tools.ietf.org/html/rfc5234
[4]: https://tools.ietf.org/html/rfc7405
@@ -0,0 +1,65 @@
sequenceDiagram
participant A as Alice
participant AA as Alice's<br>agent
participant AS as Alice's<br>server
participant BS as Bob's<br>server
participant BA as Bob's<br>agent
participant B as Bob
note over AA, BA: status (receive/send): NONE/NONE
note over A, AA: 1. request connection<br>from agent
A ->> AA: createConnection
note over AA, AS: 2. create Alice's SMP queue
AA ->> AS: NEW: create SMP queue<br>allow sender to secure
AS ->> AA: IDS: SMP queue IDs
note over AA: status: NEW/NONE
AA ->> A: INV: invitation<br>to connect
note over A, B: 3. out-of-band invitation
A ->> B: OOB: invitation to connect
note over BA, B: 4. accept connection
B ->> BA: joinConnection:<br>via invitation info
note over BA: status: NONE/NEW
note over BA, AS: 5. secure Alice's SMP queue
BA ->> AS: SKEY: secure queue (this command needs to be proxied)
note over BA: status: NONE/SECURED
note over BA, BS: 6. create Bob's SMP queue
BA ->> BS: NEW: create SMP queue<br>allow sender to secure
BS ->> BA: IDS: SMP queue IDs
note over BA: status: NEW/SECURED
note over BA, AA: 7. confirm Alice's SMP queue
BA ->> AS: SEND: Bob's info without sender's key (SMP confirmation with reply queues)
note over BA: status: NEW/CONFIRMED
AS ->> AA: MSG: Bob's info without<br>sender server key
note over AA: status: CONFIRMED/NEW
AA ->> AS: ACK: confirm message
AA ->> A: CONF: connection request ID<br>and Bob's info
A -> AA: allowConnection: accept connection request,<br>send Alice's info
note over AA, BS: 8. secure Bob's SMP queue
AA ->> BS: SKEY: secure queue (this command needs to be proxied)
note over BA: status: CONFIRMED/SECURED
AA ->> BS: SEND: Alice's info without sender's server key (SMP confirmation without reply queues)
note over AA: status: CONFIRMED/CONFIRMED
note over AA, A: 9. notify Alice<br>about connection success<br>(no HELLO needed in v6)
AA ->> A: CON: connected
note over AA: status: ACTIVE/ACTIVE
note over BA, B: 10. notify Bob<br>about connection success
BS ->> BA: MSG: Alice's info without<br>sender's server key
note over BA: status: CONFIRMED/CONFIRMED
BA ->> B: INFO: Alice's info
BA ->> BS: ACK: confirm message
BA ->> B: CON: connected
note over BA: status: ACTIVE/ACTIVE
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

@@ -0,0 +1,74 @@
sequenceDiagram
participant A as Alice
participant AA as Alice's<br>agent
participant AS as Alice's<br>server
participant BS as Bob's<br>server
participant BA as Bob's<br>agent
participant B as Bob
note over AA, BA: status (receive/send): NONE/NONE
note over A, AA: 1. request connection<br>from agent
A ->> AA: createConnection
note over AA, AS: 2. create Alice's SMP queue
AA ->> AS: NEW: create SMP queue
AS ->> AA: IDS: SMP queue IDs
note over AA: status: NEW/NONE
AA ->> A: INV: invitation<br>to connect
note over A, B: 3. out-of-band invitation
A ->> B: OOB: invitation to connect
note over BA, B: 4. accept connection
B ->> BA: joinConnection:<br>via invitation info
note over BA: status: NONE/NEW
note over BA, BS: 5. create Bob's SMP queue
BA ->> BS: NEW: create SMP queue
BS ->> BA: IDS: SMP queue IDs
note over BA: status: NEW/NEW
note over BA, AA: 6. confirm Alice's SMP queue
BA ->> AS: SEND: Bob's info and sender server key (SMP confirmation with reply queues)
note over BA: status: NEW/CONFIRMED
AS ->> AA: MSG: Bob's info and<br>sender server key
note over AA: status: CONFIRMED/NONE
AA ->> AS: ACK: confirm message
AA ->> A: CONF: connection request ID<br>and Bob's info
A ->> AA: allowConnection: accept connection request,<br>send Alice's info
AA ->> AS: KEY: secure queue
note over AA: status: SECURED/NONE
AA ->> BS: SEND: Alice's info and sender's server key (SMP confirmation without reply queues)
note over AA: status: SECURED/CONFIRMED
note over BA, AA: 7. confirm Bob's SMP queue
BS ->> BA: MSG: Alice's info and<br>sender's server key
note over BA: status: CONFIRMED/CONFIRMED
BA ->> B: INFO: Alice's info
BA ->> BS: ACK: confirm message
BA ->> BS: KEY: secure queue
note over BA: status: SECURED/CONFIRMED
BA ->> AS: SEND: HELLO message
note over BA: status: SECURED/ACTIVE
AS ->> AA: MSG: HELLO: Alice's agent<br>knows Bob can send
note over AA: status: SECURED/ACTIVE
AA ->> AS: ACK: confirm message
AA ->> BS: SEND: HELLO
note over A, AA: 8. notify Alice<br>about connection success
AA ->> A: CON: connected
note over AA: status: ACTIVE/ACTIVE
BS ->> BA: MSG: HELLO: Bob's agent<br>knows Alice can send
note over BA: status: ACTIVE/ACTIVE
BA ->> BS: ACK: confirm message
note over BA, B: 9. notify Bob<br>about connection success
BA ->> B: CON: connected
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

Some files were not shown because too many files have changed in this diff Show More