Custodial by design
Memma is a custodial platform. You sign in with an email and a password rather than a wallet, and the platform runs the chain infrastructure on your behalf — the address on your account, the deposits that fund it and the payouts that leave it are all managed by us. There is nothing to install and no seed phrase to write down or lose.
Your balance is denominated in SOL and is backed by what you have deposited. Anything you send in another asset is converted at the rate quoted when your top-up is issued — the figure on the screen is the figure credited.
The trade-off of custody is stated plainly: you are trusting the platform with the funds you deposit, the way you trust an exchange. The rest of this page is about what stands behind that trust.
How funds are handled
No keys live on the web server. The application that serves this site cannot sign a transfer on its own: there is no private key in its database, its configuration or its code — the schema has no column one could be written to, and that is deliberate. Platform funds are held on addresses whose keys stay offline, out of reach of anything an attacker could compromise remotely.
Every movement of money passes a human. A deposit is credited after an operator verifies the transaction on the network it arrived over — the hash you paste is checked against an explorer, not taken on faith. A withdrawal is reviewed and sent the same way. Slower than a fully automatic exchange, and deliberately so: an endpoint that moves money on its own is the thing that gets drained when something goes wrong.
Deposits and withdrawals are capped per transaction and per day, and every destination address is validated against its own chain's checksum before a request is accepted — a payment sent to a mistyped address is the one mistake nobody can undo.
Passwords
Your password is hashed with argon2id on the server and the hash is what gets stored. Argon2 is deliberately slow and deliberately memory-hungry — roughly 19 MB and two passes per attempt — so that somebody holding a stolen copy of the table has to spend real hardware on every guess. The plaintext is never written down and never leaves the request that carried it.
Signing in answers the same way, and takes the same time, whether the account exists or not. Answering “no such account” faster than “wrong password” would turn the login form into a way to find out who is registered here.
Five wrong attempts on one account from one caller stop it answering for fifteen minutes. The limit is keyed to both together on purpose: keyed to the account alone, anybody could lock you out of your own account by failing on your behalf. Because the caller half of that key rests on a header the caller can write, there is a second limit keyed to the account alone — sixty an hour, loose enough not to become the lockout the first limit avoids, tight enough to be no use for guessing.
You can change your password from Settings, and there is no way to recover a forgotten one. Changing it asks for the one you have now — a session on a borrowed screen should not be enough to take an account — and signs out every other device, since the usual reason to change a password is a suspicion that somebody else is already signed in. Recovering a password you do not know would mean emailing you a link, and this project has no mail service, so it does not offer one. The same goes for changing the address on an account.
Even so, do not reuse a password here. This is a portfolio project run by one person on one server. The hashing is what a live product would do; the operational care around it — audits, on-call, a team that notices a breach — is not. And since a forgotten password cannot be recovered, an account whose password is lost is lost with it.
Sessions and devices
Signing in issues a random 32-byte token, kept in a cookie marked HttpOnly so that no script on the page can read it, and SameSite so that another site cannot make your browser use it. The server stores only a hash of that token: if the sessions table were ever read by somebody who should not have it, they would hold a list of hashes rather than a set of working keys to every account.
Sessions last thirty days from when they are opened. Using one does not extend that deadline — a token that never expires while it is being used is a stolen token that never expires.
Settings → Devices lists every browser currently signed in and ends any of them on the spot. Revoking is immediate: the session stops working on the next request, not when the other browser next reloads.
Two-factor authentication
You can turn on a second factor under Settings → Security. It is standard TOTP — RFC 6238, the same thing every bank and every code-hosting site uses — so Google Authenticator, 1Password, Aegis, Authy or anything else that reads an otpauth:// QR will work with it.
The secret lives on the server and the browser never receives it again. It is shown once, during enrolment, so you can scan it; after that the only thing the browser can do is submit six digits and be told whether they were right. That is the property that makes a second factor a second factor, and it is the one an in-browser implementation cannot have.
A correct password on an account with a second factor does not open a session. It buys a ten-minute claim on being asked for a code, and that claim cannot read the account. Codes are accepted one step either side of now for clock drift, and refused once used — a code read over your shoulder is worth nothing a second time. Five wrong codes lock the factor for a minute, and the count is kept by the database, so guessing in parallel does not slip past it.
Ten recovery codes are issued once and stored only as hashes; each works once, and spending one is a single operation, so the same code cannot be redeemed twice by two requests arriving together. Turning the factor off, or replacing the codes, requires a current code — checked by the server, not merely asked for by the screen.
What is stored, and where
In a PostgreSQL database on a server rented for this project: your email, your argon2id password hash, the address the product issued you, your balance and deposit history, your handle and notification preferences, your second factor if you enrolled one, and one row per sign-in.
Each sign-in row keeps the IP address it came from, along with the browser and operating system. That is personal data and it is new — before this platform had a server it kept nothing about you anywhere. It is there so that the Devices screen can help you recognise a sign-in that is not yours, and the screen deliberately shows you a coarse version — the device and, when it can be resolved, the city — rather than the address itself.
Your browser still keeps a few things of its own, and they never reach the server: your wizard draft, your language choice, and the key that lets the support widget find your conversation again. Clearing your browser data removes those.
There is no analytics, no advertising, no third-party tracking and no profiling. Deleting your account deletes its sessions, deposits and second factor with it.
Where this is still weak
A page claiming to be honest about security owes a list of what it has not solved, not only a list of what it has.
- Deposit confirmation is manual. There is no automatic chain watcher: an operator verifies your transaction hash on an explorer and credits the balance by hand. That keeps a machine from being talked into crediting anything, and it means crediting takes minutes rather than seconds — the queue moves at the speed of a person.
- Email addresses are unverified. Nothing is sent to them yet, so nothing checks that yours is yours. Password recovery is not built for the same reason — which makes your password the one thing standing between you and your balance. Use a password manager.
- The sign-in throttle lives in one process's memory. It resets when the server restarts. It is a speed bump against guessing, not a wall.
Reporting a problem
If something here looks wrong — a balance that does not add up, a screen that lets you do what it should not — write to support from the widget in the corner. A report that names steps to reproduce is read first.