Skip to content

Verified Frontends

Verified Frontends binds one of your domains to a publishing key that you hold. After that, only a record signed by that key can change what the domain serves.

Verified Frontends is rolling out to protocol teams first. If you want it on your account, write to hello@stasho.xyz.

What it protects

The attack this closes is a takeover of the things around your site: your DNS zone, your registrar account, your hosting account. Any of those lets someone point your domain at a build you never made. With a domain bound to a publishing key, the build a visitor gets is the one your key signed, and every change is a public record on Solana that anyone can read back. Binding is deliberately slow: a 48 hour cool-down runs between proving control of the domain and the binding going live, and a Stasho operator reviews it before it does. A zone stolen on Monday cannot be bound and used on Monday.

The publishing key

The key is generated in your browser during setup. It is shown once, and Stasho never receives the secret half. Store it the way you store any deploy secret: a password manager, then your CI's secret store.

  • Stasho keeps the public address only. It is what the on-chain record is checked against.
  • One key can cover several domains, so a project with a marketing site and an app does not need two.
  • If the key leaks, or you rotate your CI secrets, transfer the domain to a new key. There is no way to recover a lost secret; a transfer is the recovery path.

Publishing

A record says "this domain serves this CID". There are three ways to write one.

The wizard's first record. The last step of setup publishes a record for your current live deployment. If the key is still in the browser tab that generated it, the wizard signs there and you do nothing else.

Your GitHub workflow. When the domain belongs to a Stasho project, the deploy workflow Stasho generates carries a step named Publish verified frontend record (<your domain>). Add your key as a repository secret named STASHO_VF_KEY (Settings, then Secrets, then Actions) and every deploy publishes a record on its own. If the step is missing, save any setting on the project's Settings page to re-sync the workflow.

The CLI. @stasho/vf publishes from anywhere the key is set in the environment:

sh
export STASHO_VF_KEY=...   # your key, never an argument
npx --yes @stasho/vf@0.1.0 publish --domain app.example.com --dir ./dist

--dir computes the CID of the exact folder you deployed; nothing is uploaded. Pass --cid instead when you already know the CID Stasho is serving. Pin the version; a newer CLI may not match this backend.

The CLI checks the message it is asked to sign before signing it, so a backend that returned the wrong CID gets a refusal rather than a signature. Stasho pays the transaction fee; your key only signs.

A non-zero exit is worth reading rather than retrying. Exit 3 means the server refused the request, which covers a key that does not match the bound one, an account without verified publishing, a rate limit, and a wrong binding state alike. The message the CLI prints names which.

Every publishing surface in the app also offers a Copy prompt for your agent button. It copies the whole task as instructions, including the rule that the key belongs in the environment and must never be pasted into a chat. Hand it to Claude Code, Cursor, or any agent that can run shell commands.

The out-of-band proof

Setup asks you for a link to a proof, and the app gives you the exact line to publish:

Stasho publishing key for app.example.com: <your key address>

Put it somewhere an operator can see belongs to you and that your DNS zone does not control: a VERIFIED.md or .well-known/stasho-vf.txt file in your project's public GitHub repository, or a post from your project's official account. Then paste the link into the form.

The reason it has to sit outside DNS: the TXT record proves you control the zone, but so would someone who had just stolen the zone. The proof shows the same key from a place a zone hijacker cannot edit, which is what lets the operator tell the two apart. A missing link does not block the review, it just slows it down.

Transfer and release

Both are signed by the key bound today, and both wait on an operator before they take effect.

sh
npx --yes @stasho/vf@0.1.0 transfer --domain app.example.com --new <new key address>
npx --yes @stasho/vf@0.1.0 release --domain app.example.com

A transfer hands the domain to a new key. The current key keeps publishing until the operator confirms, so a transfer never leaves the domain unable to publish. A release unbinds the domain: Stasho writes the unbind on chain after the operator confirms, and the domain goes back to being an ordinary custom domain.

The status page

Each domain has a status page under Verified in the sidebar.

CardWhat it tells you
Domain and stateThe domain heads the card. Under it, where the binding is: Waiting for DNS, Cooling down, Awaiting review, Bound, Transfer pending, Releasing, Released, or Rejected. A rejected binding carries the operator's note and a link to start over.
Publishing keyThe bound key's address, and the incoming one while a transfer is pending.
DNS tripwireThe _stasho-vf.<domain> TXT record and its value, plus Re-check now while the binding is waiting on DNS or cooling down. Below it, once an operator has approved the binding, the snapshot of your zone's NS, A, AAAA and CNAME records taken at that approval. A later change to those is what the tripwire catches.
RecordsEvery record the indexer has seen for this domain, newest first, each linking to the transaction on the Solana explorer.
Publishing from CIThe agent prompt and the generated workflow step for this domain.
Transfer or releaseThe prompts and commands above.
Out-of-band proofThe line to publish and the link you gave for it.

Verification

A binding is fully working when:

  • The status page shows Bound
  • The Records table lists a pub record for your current deployment
  • A new deploy adds another pub record within a few minutes of the workflow run finishing