Skip to content
Solana Devnet: test network. Documents and acceptances here are for testing, not production evidence.

Verifiable agreements on Solana

Every “I agree” on the internet is a row in someone's database.

Stele turns it into proof.

Stele records exactly which version of your terms each person accepted — signed by them, anchored on Solana, checkable by anyone, forever. And it lets Solana programs refuse anyone who hasn't accepted.

1,000 recordings free every month · Free for your users · Open-source verifier

Today · a database row

Stored
user_id284
accepted_termstrue
version3
accepted_at2026-03-02

Kept — and editable — by the company itself.

With Stele · on Solana

Signed
documentTerms of Service v3
fingerprint9f2c 41aa 07de…
signed bythe user's passkey
recordedSolana slot 312,448,901

No one can change it — there is no edit.

Today: a user ticks “I agree”. The company stores a row.

0 SOL

for your users — Stele pays the network fee

Seconds

to accept with Face ID, a passkey or a wallet

1 call

in your Solana program to enforce terms

Forever

verifiable — even with Stele's servers switched off

The problem today

Online agreements run on trust. That breaks the moment it matters.

Every day, people accept terms to open accounts, invest, trade and play. When something goes wrong, the only record of what they agreed to is whatever one party kept.

01

“I agree” becomes a database flag

accepted = true says nothing about what was accepted — and the record is kept, and can be edited, by the company that may be on the other side of a dispute.

The exact version, signed by the user, recorded on Solana.

02

Terms change silently

Version 3 becomes version 4 overnight. Months later, nobody can show which text a given person actually saw and accepted.

Every version is fingerprinted on-chain. History cannot be rewritten.

03

Disputes ask: what did the user see?

Courts have refused to enforce online terms when the company could not show the user was presented with them — Specht v. Netscape (2002), Nguyen v. Barnes & Noble (2014).

The user signs the very text on their screen. The receipt contains it.

04

Screenshots and logs prove nothing

A screenshot can be faked and a log can be edited. Evidence that only works if you trust one party is not evidence.

Anyone verifies it against Solana — without Stele, without the company.

05

Website checks can be bypassed

On-chain apps ask for terms in their website. Anyone can call the smart contract from a script and skip the checkbox entirely.

Stele Gate: the program itself refuses wallets that have not accepted.

06

Regulated assets need proof per investor

Tokenized funds and real-world assets are expected to show that each investor accepted the current offering terms — for every deposit, at audit time.

Each wallet's passport names the version it accepted, checked on every deposit.

How Stele solves it

Three steps. Evidence nobody can rewrite.

No new habits for your users, and no new infrastructure for you: a widget on your page, and one call in your Solana program if you want enforcement.

  1. 1

    Publish

    The organization publishes its terms. Stele fingerprints each version on Solana — the text can never be swapped afterwards.

  2. 2

    Accept

    The user signs the exact text with Face ID, a passkey or a wallet. Free for them, a few seconds, no crypto needed.

  3. 3

    Prove & enforce

    Anyone verifies the receipt against Solana, forever. Solana programs refuse wallets that have not accepted the current version.

Who it is for

Anywhere an agreement must hold up later.

Tokenized funds & RWA

Offering terms accepted before every deposit, provable per investor.

DeFi with compliance

Permissioned pools and vaults that enforce terms in the program.

SaaS & marketplaces

Terms of service and privacy policies with evidence that holds up.

Fintech & payments

Consents and purchase confirmations signed by the customer.

DAOs & communities

Membership agreements and codes of conduct, per wallet.

Games & apps

EULAs and rules accepted once, enforced on-chain where it matters.

Stele Gate

Require current acceptance before a protected Solana action.

An RWA app requires its current offering terms before a deposit. The investor's passport proves current acceptance, and the Solana program checks it directly.

Explore Gate
Stele Gate — a protected depositIllustration
atlas.fund7xKX…9f3A

Atlas Real Estate Vault

Commercial property income · 7.2% target

Deposit

5,000 USDC

InvestorDeposit 5,000 USDC
Vault programdeposit()
Passport checkaccepted?
Depositwaiting

Passport

Missing

Program check

Not run

Acceptance and deposit are two transactions: the investor sees one “Accept & Deposit” step.

1/7An investor taps “Deposit 5,000 USDC” in the Atlas Real Estate Vault app.

Stele Proof

Prove which exact version a user accepted.

A company publishes exact terms. A user accepts them. Stele preserves evidence of exactly what was accepted — and anyone can verify it later.

Explore Proof
Stele Proof — how evidence is madeIllustration
Companypublishes v3
FingerprintSHA-256 on Solana
User acceptspasskey · wallet
Stele evidencesignature + time
Verificationby anyone, later
Terms of ServiceVersion 3

1/6The organization publishes Terms of Service, version 3.

Example · Atlas Real Estate Vault

Investors can't deposit until they've accepted your current terms.

A permissioned investment vault on Solana, end to end. It is also the live demo — every step is a real transaction.

  1. Deposit 5,000 USDCThe investor connects a wallet and taps Deposit in the Atlas Real Estate Vault.
  2. Not accepted → the modal opensInvestor Offering Terms v1, hash-verified against Solana. “Accept & Deposit”.
  3. Acceptance recorded, deposit succeedsThe acceptance is added to the investor's passport; the vault's check passes.
  4. The fund publishes v2 and requires itThe investor's next deposit is rejected on-chain: their passport names v1.
  5. Accept v2, deposit succeedsThe same passport now names v2. The v1 acceptance stays verifiable forever.
  6. A direct CLI call failsNo frontend, no SDK — deposit() without a current passport is rejected by the program.

The whole integration in the vault program

rust
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
    stele_gate::require_current_passport(
        &ctx.accounts.passport,
        &ctx.accounts.policy,
        &ctx.accounts.investor,
    )?;
    // … record the deposit
    Ok(())
}

Stele never holds the investor's funds and is not in the deposit's execution path: the vault program reads the passport and the policy itself.

Run it on Devnet

Why program-level

Your frontend can be bypassed. The program check cannot.

A terms checkbox only exists in the interface you built. Anyone can call a Solana program from a script, the CLI or another frontend — so that is where the requirement has to live.

Can the requirement be skipped?Illustration

Without a program-level gate

Normal frontendAllowed
User
Checkboxfrontend
Program
Script / custom client
Script / CLI
Checkboxskipped
Programexecutes anyway

With Stele Gate

Script / custom client
Script / CLI
Vault program
Passport checkaccepted?
Investor who accepted the current terms
User
Passport checkcurrent version ✓
Vault programdeposit executes

The frontend is optional. The program check is not.

1/4Without program enforcement, the terms checkbox lives in the frontend. A normal investor passes through it.

For developers

Integrate without rebuilding your app.

Gate is one call in your program and one wrapper around the action you already send. Proof is two lines on any page.

ts
import { createSteleGate } from "@stelehq/sdk";

const stele = createSteleGate({
  apiBaseUrl: "https://stele.site/api",
  // Any Wallet Standard wallet: the key that signs the deposit.
  wallet: {
    address: wallet.publicKey.toBase58(),
    signMessage: (message) => wallet.signMessage(message),
  },
});

// Shows the Stele modal only if the investor's passport has no current entry
// for the policy's document, records the acceptance, then sends the deposit.
const signature = await stele.gate({
  policy: OFFERING_TERMS_POLICY, // the policy address your program pins
  action: () => sendDepositTransaction(5_000_000_000n),
  acceptLabel: "Accept & Deposit",
});

Stele Proof · any website

html
<!-- Stele Proof: where your terms should appear -->
<div data-stele-organization="acme.com" data-stele-document="terms-of-service"></div>
<script src="https://stele.site/sdk/v1/stele.js" defer></script>

Choose

Which Stele do you need?

I run a normal application or website.

Use

Stele Proof

I want

  • Verifiable terms acceptance
  • Evidence of the exact document version
  • Receipts anyone can verify
  • Passkey or wallet signatures
  • An audit trail you don't have to vouch for
Use Proof

I run a Solana application.

Use

Stele Gate

I want

  • Everything in Proof
  • One passport per wallet
  • Policy version enforcement
  • Protected Solana actions
  • SDK and program integration
  • Bypass resistance on-chain
Use Gate

Gate includes Proof.

Gate layer — Stele Gate
PassportPolicy enforcementSolana integrationProtected actions
Proof layer — Stele Proof (foundation)
DocumentsVersionsAcceptancesReceiptsVerification

Security & verification

Built for the day someone wants the record to say something else.

Who controls what

Exact versions

Each version is canonicalized and fingerprinted on Solana. There is no instruction to edit it.

Signed by the user

A passkey or wallet signs the exact text. Nobody — not Stele, not the organization — can sign for a key they don't hold.

Enforced by your program

Passports are written only by verified acceptances and checked by the program that acts. Stele never holds funds.

Verified by anyone

Receipts are checked against any archival Solana RPC with open-source tools — with Stele's servers switched off.

Stele answers “which exact document version did you accept?” — not “who are you?”. A passkey or wallet proves control of a key, not identity; Face ID is not KYC. What stays trusted: what the signing page displays, and who enrolls a passkey. Read the trust model.

Get started

See it work on Solana Devnet.

Accept offering terms, deposit, publish v2, watch the old acceptance stop working and a direct call fail — every step a real transaction.