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

Stele

Prove what was agreed.
Enforce what must be agreed.

Verifiable acceptance infrastructure for organizations and Solana applications.

The problem

“I agree” usually becomes a database flag.

When a dispute comes, the only record of what a user accepted is controlled by one of the parties. Stele replaces that row with evidence anyone can check.

The same acceptance, before and afterIllustration

Without Stele

acceptances
─────────────
user_id:  284
accepted: true

With Stele

  • DocumentTerms of Service — version 3
  • FingerprintSHA-256 9f2c 41aa 07de 5b13…
  • Signaturethe user's passkey or wallet
  • TimestampSolana slot 312,448,901
  • Receiptstele-receipt-….json

1/4Without Stele, the record of an agreement is a row in the company's own database.

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.

Stele Gate

Require current acceptance before a protected Solana action.

An RWA app requires its current offering terms before a deposit. The investor's AccessPass 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()
require_current()AccessPass?
Depositwaiting

AccessPass

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.

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. No AccessPass → the modal opensInvestor Offering Terms v1, hash-verified against Solana. “Accept & Deposit”.
  3. Acceptance recorded, deposit succeedsThe acceptance creates the AccessPass; the vault's require_current() passes.
  4. The fund publishes v2 and requires itThe investor's next deposit is rejected on-chain: their pass names v1.
  5. Accept v2, deposit succeedsThe same pass now names v2. The v1 acceptance stays verifiable forever.
  6. A direct CLI call failsNo frontend, no SDK — deposit() without a current pass 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(
        &ctx.accounts.access_pass,
        &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 AccessPass and the policy itself.

Run it on Devnet

Versioning

Publish v2 and old access no longer satisfies the requirement.

An AccessPass names the version its holder accepted. When the organization requires a newer version, older passes simply stop satisfying the policy — nothing is rewritten.

When the policy changesIllustration

Published versions

Offering Terms v1Required

fingerprint 72d7 1ed9 83fa…

The wallet's AccessPass

AccessPass

accepted: Offering Terms v1

Valid

Protected deposit

deposit(5,000 USDC)

Waiting

—

The evidence for v1 remains. Access now requires v2.

1/6The investor accepted Offering Terms v1. Their AccessPass names v1 — and the policy requires v1.

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
require_current()AccessPass?
Investor who accepted the current terms
User
require_current()AccessPass valid
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 AccessPass is missing or
// outdated, 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
  • AccessPass for each wallet
  • Policy version enforcement
  • Protected Solana actions
  • SDK and program integration
  • Bypass resistance on-chain
Use Gate

Gate includes Proof.

Gate layer — Stele Gate
AccessPassPolicy 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

AccessPasses 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 pass stop working and a direct call fail — every step a real transaction.