DEV Community

Cover image for What a Farcaster mini app can and can't do inside Quilcast
borodutch.eth 💎🤲
borodutch.eth 💎🤲

Posted on Originally published at blog.quilcast.com

What a Farcaster mini app can and can't do inside Quilcast

When a Farcaster mini app runs inside Quilcast, it lives in a sandboxed iframe on its own origin and can only use 26 named calls that Quilcast answers on purpose; everything else is refused with an error. Wallet requests go through a second layer of checks that refuses transactions to Farcaster's account contracts, signatures that would log you in to Farcaster or Warpcast, and any address that isn't the one you connected. Here is how that works, what I ran to confirm it, and what a mini app still has to ask you for.

I read the code at commit 94dd385 of the Quilcast repository (version 0.45.10, 2026-11-01) and ran its guard tests and a few requests of my own on Node 22.23.4. I did not open a real mini app in a browser for this post; everything below is from the source and those runs.

What is a Farcaster mini app host?

A mini app is a web page that a Farcaster client opens inside itself. The page talks to the client through the official SDK, @farcaster/miniapp-sdk, which sends messages up to whoever embeds it: sign this in, open that profile, send this transaction. The embedding client is the host, and it decides what each message is allowed to do.

Quilcast is a host. Tapping a mini app's card in a cast opens the app inside Quilcast, and so does pasting its link under Mini apps. Several can run at once: one is on screen and the rest wait in a dock with their iframes alive. Mini apps arrived in version 0.10.0 on 2026-10-21, a day after the first commit, and the checks below grew over the days after.

How does the iframe sandbox work?

Each app gets an iframe with sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox", no top-level navigation and no access to Quilcast's own page, because it runs on its own origin. Quilcast refuses any address that isn't https. The allow attribute starts as clipboard-write; web-share. Camera and microphone are added only after you approve them for that app, and the iframe reloads once so the browser applies it. The app's menu has a "Take back camera and mic" item that removes the permission again.

Messages from the iframe are accepted only when three things hold. The sender must be the exact contentWindow of an app Quilcast opened, the message's origin must match the origin the app was opened at, and the call path must have exactly one segment. Arguments are read only as plain values: the SDK speaks Comlink, and anything that tries to pass a proxy or a MessagePort is dropped to undefined.

Which SDK calls does Quilcast answer?

A table in src/miniapp.tsx called calls lists 26 names, and a request for anything else is rejected with "Quilcast doesn't support ." The list covers ready, close, openUrl, openMiniApp, viewCast, viewProfile, composeCast, signIn, addMiniApp, the primary button and back button, haptics, camera and microphone, the Ethereum and Solana wallet providers, and the token actions viewToken, sendToken and swapToken.

Every call checks its arguments before acting. viewCast wants a 40-character hex hash, and viewProfile wants a positive safe integer for the FID. The primary button label is cut to 40 characters. signManifest, which would let an app sign a Farcaster domain manifest with your account, is answered with rejected_by_user without asking anyone.

Prompts follow one more rule. The calls that need your attention (signing, sending, opening links, adding an app) only work for the app that's on screen and visible: from the background they throw Open <app> to continue, so a hidden app can't put a question in front of you while you use another one. An app can also have only one question open at a time.

What can a mini app do with my wallet?

The Ethereum side is the one I wanted to read most closely. In src/guard.ts there are three lists: 16 read methods such as eth_call, eth_getBalance and eth_getLogs, which are answered from a public RPC for the app's chain with no prompt at all; 5 signing methods (personal_sign, eth_signTypedData_v4, eth_sendTransaction, wallet_sendCalls and wallet_watchAsset), which always go to a wallet that asks you; and 2 wallet reads. Methods like eth_sign (blind hashes), eth_signTransaction and the older typed-data versions are not on any list, so the app gets an error with code 4200 instead.

Your wallet only ever sees quilcast.com as the requester, so the per-site checks a wallet would normally make have to happen in Quilcast. checkWallet does them before a request reaches the wallet. I ran it on 2026-11-01 with a connected address and a mini app on app.example, and these are the results:

Request from the app Result
Send a transaction to Farcaster's IdRegistry Refused, 4100: "Mini apps can't send transactions to Farcaster's account contracts."
personal_sign for a different address Refused, 4100: "That address isn't the connected one."
Sign-in message for warpcast.com Refused, 4100: "Mini apps can't sign you in to warpcast.com."
A message containing farcaster://fid/ Refused, 4100: "Mini apps can't ask for Farcaster account signatures."
Sign-in message for other.xyz Allowed only after Quilcast asks you whether to sign in to that other site
A plain transaction to an unrelated address Passed on to your wallet, which asks you

The refused contracts are the eight Farcaster ones on Optimism: IdRegistry, IdGateway, KeyRegistry, KeyGateway, StorageRegistry, Bundler, SignedKeyRequestValidator and RecoveryProxy. Those are how a Farcaster account changes hands, adds a key or changes recovery, so no mini app has a reason to send to them. Typed data whose domain name contains "farcaster" is refused for the same reason.

The sign-in row matters because one app could otherwise sign you in somewhere else as you. A Sign-In With Ethereum message names the site it is for. If that site is the app's own host, it goes ahead; if it is the Farcaster family (farcaster.xyz, warpcast.com, quilcast.com), it is refused; for any other site Quilcast asks you first. The same check covers Solana messages.

How does Quilcast warn me about approvals and permits?

The approval prompts say what a request does in words. tokenCall reads the first four bytes of a transaction's data and recognizes approve, increaseAllowance, setApprovalForAll, Permit2's approve, transfer, transferFrom and both safeTransferFrom forms, then decodes the arguments. I ran an approve with the maximum amount through it and it was decoded as an approval whose amount is past the 2^128 line that the code treats as unlimited. typedRisk does the same for signatures: a Permit or Permit2 message comes back as a permit with its spender, and a Seaport order comes back as a listing, so the prompt can say "this lets someone else take tokens" or "this puts NFTs up for sale" rather than showing a hex blob.

The repository's own tests cover this file: node --test src/guard.test.ts passed 6 of 6 for me, including the cases for refused Farcaster signatures, handed-back sign-ins for other sites, and the spelled-out token calls.

Is Quilcast itself a mini app?

Yes, and that part is small. quilcast.com/.well-known/farcaster.json answers 200 today with a signed account association and a manifest whose home URL is https://quilcast.com/, and the page has fc:miniapp and fc:frame meta tags, so a link to Quilcast shows an "Open in Quilcast" card in Farcaster and Base clients. The code that makes it behave inside those clients, src/inapp.ts, is 18 lines: it tells the client the page is ready, hooks the client's back button, uses the client's wallet as window.ethereum and sends links out through the client, because a webview may not open new windows. The app loads that file only inside a Farcaster or Base client, so nobody else downloads the SDK.

Since version 0.28.1 a Quilcast link also opens as a mini app inside Quilcast, and 0.28.2 made that work to any depth. Chrome won't load a frame at the same address as its ancestors, so each nested copy gets its own ?in= parameter.

What I would check in any mini app host

If you build a host, or you're picking a client, these are the questions that this code answers and that I would ask of any other:

  1. Does the host answer a fixed list of calls, or does it forward whatever the app sends?
  2. Can a message come from anything other than the app's own frame and origin?
  3. Can an app reach the Farcaster account itself, through contract calls or signatures, and not only the wallet?
  4. Does the prompt say what a token call or a permit does, or only show the raw request?
  5. Can a background app put a prompt in front of the one you're using?

Quilcast is open source, so each answer above can be checked against src/miniapp.tsx and src/guard.ts. To see it working, open any mini app from a cast in Quilcast, a Farcaster client for Hypersnap that runs in your browser, on iPhone and on Android.

Source read and tests run on 2026-11-01 with Quilcast 0.45.10 (commit 94dd385), Node 22.23.4 and the dependencies' latest versions at install time. The mini app SDK version the repository declares is ^0.3.0.

Top comments (0)