Wallet keys are stored separately
Private keys never reach the servers that handle requests from the internet. Keys are generated and stored only in a separate signing service. It accepts no incoming connections from the internet, and the keys inside it are encrypted with AES-256-GCM using a key from Vault, which runs on its own separate server.
A key is decrypted in memory only for the duration of a single signature and is wiped immediately afterwards. The rest of the platform sees only public addresses.
The signing service checks every operation on its own
Even if someone gained access to the main server, the signing service would not sign an arbitrary transaction. It checks every request independently:
- per-transaction, hourly and daily limits for each coin;
- funds collected from payment addresses can only be sent to the service’s own wallet;
- coins without configured limits are not signed at all;
- each request is signed no more than once and is logged together with the decision;
- signing can be frozen instantly for the whole service or for a single wallet.
A payment is credited only after a double check
Moonvex is connected to several independent node providers on every network. A payment is credited only once two different providers have confirmed the transaction. A failure or tampered data at one provider cannot credit money that does not exist.
Every on-chain transfer is counted exactly once. This is guaranteed by a unique key in the database, not just by application code.
Double-entry accounting
All movements of money are recorded in a double-entry ledger. The ledger is append-only. The database itself checks that entries balance: it will not accept an unbalanced operation, even if there is a bug in the code.
The service regularly reconciles its books against the actual on-chain wallet balances. Any discrepancies go into a separate queue and are investigated manually.
Payouts: 2FA and limits
- every payout from the dashboard or the Telegram bot is confirmed with a one-time code from a 2FA app;
- a code cannot be reused, and the number of attempts is limited;
- payouts are subject to per-transaction and daily limits, so even if a password leaks, the whole balance cannot be withdrawn quickly;
- API payouts require an idempotency key, so retrying a request after a timeout will not create a second payout.
Account protection
- passwords are stored only as argon2id hashes;
- two-factor authentication (TOTP) in the dashboard, with 2FA secrets stored encrypted;
- lockout after a series of failed sign-ins, and request rate limiting;
- team roles: owner, developer, accountant, viewer. Each person sees and does only what their role allows;
- API keys are stored as hashes and support permissions, expiry, revocation and an IP allowlist.
Signed notifications
Every webhook is signed with HMAC-SHA256 and includes a timestamp. Your server can verify that a notification came from Moonvex and was not altered or replayed. Delivery is retried for up to three days, and the delivery log is visible in the dashboard.
Webhooks are sent only to https addresses. Internal and reserved addresses are blocked after DNS resolution, and redirects are not followed, so webhook settings cannot be used to attack an internal network.
Audit log
Administrator actions such as changes to fees and limits, freezes and payouts are recorded in a log where each entry is linked to the previous one by a SHA-256 hash. An entry cannot be deleted or quietly altered after the fact: the chain would no longer match up. Access to the admin panel is allowed only from trusted networks and requires 2FA.
What you can do
- turn on 2FA in the dashboard; you cannot withdraw funds without it;
- always verify the webhook signature, and never mark an order as paid based on the customer’s redirect alone;
- keep your secret API key on the server only, never in website or mobile app code;
- restrict your API key to your own IP addresses;
- give team members only the roles they actually need.
Found a vulnerability?
Write to us before any public disclosure and describe how to reproduce the issue. Do not access other people’s data or disrupt the service while testing. We will reply, fix the issue and let you know when the fix is released. The contact is listed in /.well-known/security.txt.
Contact us
Security questions and vulnerability reports: [email protected]. We reply to every message.
Terms of service →