Security
WHAT PROTECTS YOU.
Your funds never move into an Asher account. The Genesis path refuses by default. The participant record contains no personal data. On-chain history cannot be rewritten, not even by us. Each claim below is stated in your terms first and the implementation second.
Custody
Asher never holds your money
This is the claim that matters most, and it is structural rather than procedural. There is no account to deposit into, so there is nothing to lose custody of.
Your funds stay in your account
Execution is non-custodial. The money stays in your own account at the venue — it is never moved into an account Asher controls.
- No deposit into an Asher account
- No managed account
- No vault
The key that trades cannot withdraw
Orders are signed by a session key. That key can place an order and cancel an order. It cannot withdraw. The boundary is in what the key is permitted to do, not in a promise about how it will be used.
- Place: yes
- Cancel: yes
- Withdraw: no
Nothing is pooled and nobody trades for you
There is no copy-trading and no performance carry. Asher does not take a cut of your results, because it is not positioned to hold or to move your capital in the first place.
- No copy-trading
- No performance carry
Genesis
The contribution path refuses by default
Genesis is closed. It is closed because of what the code requires before it will open, not because a switch happens to be off — and the difference is the whole point of this section.
- Contributions are disabled
- The check that opens a contribution requires three things at once: production status, an explicit live flag, and well-formed, non-zero contract addresses. All three, not any one. A flag flipped on its own cannot open a sale.
- An address has to be a real address
- Contract addresses are validated by format, and the zero address is rejected. A truthy placeholder — the string "TBD", for instance — does not pass. Something that merely looks configured does not count as configured.
- The config is frozen at runtime
- A stray assignment cannot re-point a contract address or re-price a round, because the object holding them is frozen. The mistake that would be hardest to notice is the one that is prevented.
- Allocations are integer arithmetic
- An allocation is computed in integer arithmetic from the round’s cap ratio — never from a rounded decimal price. A fully subscribed round therefore lands exactly on its cap instead of a few units either side of it. This is asserted by tests.
- Refusal happens more than once
- The layers are independent: the control is disabled, the handler refuses the request, and the contract boundary refuses it again. Defeating one of them does not get a contribution through.
- There is no contract yet
- No production contract has been supplied, audited or deployed, and no address is published. That is the current state, stated plainly rather than left to be inferred from an empty page.
Data minimisation
The safest record is the one that was never collected
There is no identity document store here to protect, because there is no identity document in the record. A participant is a wallet address and a status value.
What the participant record does not contain
The participant data model holds no name, no email address, no postal address, no date of birth, no document number and no document image — anywhere in it. Data that is never collected cannot be leaked, mishandled or subpoenaed.
- No name
- No email address
- No postal address
- No date of birth
- No document number
- No document image
How a participant is identified
By wallet address. That is the identifier the record is keyed on, and it is the only identifier the record needs.
Compliance state is a status, not a dossier
Where a compliance check applies, what is stored is a status value. The evidence behind that status stays with the provider that collected it and is not copied here.
Operator-only fields stay operator-only
Internal fields — risk flags, internal notes — are never exposed to a participant-facing surface. The separation is in which surface can read the field, not in a UI that chooses to hide it.
Integrity of records
What was recorded stays recorded
An audit trail an operator can edit is not an audit trail. These constraints are enforced in the database, below the application, so they hold regardless of which code path is doing the asking.
- On-chain records are append-only
- A database trigger refuses any delete of an anchored row and any update to an on-chain column — including for an operator holding the service role. The person with the most privilege in the system still cannot rewrite what the chain recorded.
- Every config change is logged, in the same transaction
- Each off-chain configuration change is written to an append-only audit log in the same transaction as the change itself, recording who, what, the value before and the value after. The change and its record succeed or fail together, so a change cannot exist without its log entry.
- The admin UI separates the two
- On-chain state and off-chain configuration are presented as different things, and no admin path can write an on-chain field. An operator cannot edit a chain fact by mistaking it for a setting.
This website
The posture of the page you are reading
A security page that says nothing about itself is incomplete. This site loads no third-party code and ships a very short dependency list, and most of that is checkable from your own browser rather than taken on trust.
This site loads no third-party code
No CDN, no analytics, no tag manager, no external script, no external font. The client bundle executes first-party code only. That is also why the Content-Security-Policy is a genuine allow-list rather than a list of exceptions wide enough to be meaningless.
- No CDN
- No analytics
- No tag manager
- No external script
- No external font
Security headers on every route
A Content-Security-Policy is served on every route with frame-ancestors set to none, alongside X-Frame-Options DENY, HSTS, Referrer-Policy, Permissions-Policy and nosniff. They are configured once, in next.config.mjs, so no route can be shipped without them.
Five runtime dependencies
next, react, react-dom and the two Supabase clients. There is no wallet library and no crypto library in the runtime dependency set — a short list is a small attack surface and a reviewable one.
No secret is hardcoded
Nothing anywhere in the codebase carries a hardcoded secret. The service-role database key is read in exactly one server-only module, and that module is unreachable from any client component.
Headers served on every route
Content-Security-Policy (frame-ancestors 'none')X-Frame-Options: DENYStrict-Transport-SecurityReferrer-PolicyPermissions-PolicyX-Content-Type-Options: nosniff
Limits
What is not done yet
Everything above is in place today. The following is not, and you should weigh it accordingly.
Keep reading
What is live, and what is not
The companion to this page is the status of the ecosystem itself — which parts are shipped, which are beta, which are still being built, and what is deliberately left unmeasured.