Your own Farcaster.
Three commands.

Run a Snapchain read node on a server you rent, and post from a client that talks only to it. No API keys. No one's permission.

  1. 0
    Rent a servernode scripts/provision.mjs
  2. 1
    Start a read nodessh … "curl …/node.sh | sudo bash"
  3. 2
    Post from your own clientnode fc.mjs init --node <your-server>

Tested end to end on mainnet, October 2026: a node synced from scratch, then the first cast sent through it.

Proof

Run on mainnet, not just written.

A node synced from scratch on a rented server, then every client command went through it to the real network.

  • Gasless app key added, then the first cast
  • Cast, reply, like, recast, undo each, delete
  • Follow and unfollow, app filters on the feed
  • Each result checked against the node's own API

Full transcript  ·  59-second video, music by Kevin MacLeod (CC BY 4.0)

Get started

Up and running in about ten minutes.

Plus a few hours while the node syncs on its own. You'll need Node.js 20 or later, an Azure, AWS or Hetzner account, and a Farcaster account with storage and its recovery phrase.

Step 0

Rent a server.

git clone https://github.com/Sats100M/farcaster-in-3
cd farcaster-in-3
az login                                   # or aws login, or HCLOUD_TOKEN in .env
node scripts/provision.mjs --provider azure

It shows the price and asks before creating anything: a 4 vCPU, 16 GB server with a 2 TB disk and the Snapchain ports open. Then it prints step 1 for you.

Step 1

Start a Snapchain read node.

ssh -i ~/.ssh/farcaster_node azureuser@<server-ip> \
  "curl -fsSL https://raw.githubusercontent.com/Sats100M/farcaster-in-3/main/scripts/node.sh \
   | sudo bash -s -- --fid <your-fid>"

Installs Docker and the official Snapchain node and starts syncing. The first sync downloads a 470 GB snapshot and takes several hours. Run fc-node-status on the server to watch it.

Step 2

Post from your own client.

cd client && npm install
node fc.mjs init --node <server-ip>

Asks for your recovery phrase once, uses it to give this client its own app key, then forgets it. After that:

node fc.mjs feed
node fc.mjs cast "posting from my own node"
node fc.mjs follow dwr
node fc.mjs like 3/0x…
node fc.mjs feed --hide-app <app>

Treat your recovery phrase like rm -rf. It controls your account. Only paste it into software you've read or trust. This client uses it in memory to sign one message and never stores or sends it. You can check: it's one file.

Trying it for the first time? Use a test account. And when an app offers delegated signing, where you approve its key from an app that already holds your account, prefer that: the phrase never leaves your phone. This client asks for the phrase only because a fresh server has no app to approve from.

Finished? node scripts/destroy.mjs deletes everything, and billing stops.

How it works

No single app owns your account.

Farcaster splits a social network into three layers, run by different people. If one app bans you or disappears, your account, followers and casts are still there.

On Optimism

Identity

Your account number, the wallet that owns it, and the storage you've paid for.

On Snapchain

Data

Every cast, like and follow, each one a small signed message kept by every node.

Anyone's code

Apps

The feed you scroll and the button you press. Including the client in this repo.

Accounts

FIDs, custody addresses and storage.

FID

Your account number, issued by the ID Registry contract on Optimism. Usernames like @dwr are a separate label that points to it.

Custody address

The Ethereum wallet that owns your FID, derived from your recovery phrase at m/44'/60'/0'/0/0. It can transfer the account and add or remove app keys, so you use it to authorize things, never to post. The client finds your FID by asking your own node which account your custody address owns, so no Ethereum RPC or API key is needed.

Storage rent

Every message takes space on every node, so accounts pay rent, in yearly units on Optimism. One unit holds roughly 5,000 casts, 2,500 reactions and 2,500 follows. Go over and the oldest are pruned. Accounts made in the Farcaster app usually include storage; this client can't post for an account without it.

The network

Validators write. Read nodes follow.

Snapchain orders and stores every message. A small, known set of validators propose and vote on blocks. Read nodes run the same software with read_node = true, keep the full state and serve the same APIs. Anyone can run one.

SubmitYour client signs a cast with its app key and sends it to your node.
RelayYour node checks it and gossips it on. It keeps no mempool.
IncludeA validator picks it up and puts it in a block.
SyncThe block reaches your node, and the cast is in your feed.

So a success from your node means accepted and relayed, not yet included. Under heavy load a relayed message can get lost and need sending again.

App keys

Your client gets its own key.

Apps never post with your custody key. Each gets an app key: an Ed25519 key that can sign messages for your FID and nothing else.

Since mid-2026, adding one is free. There's no Optimism transaction: the client sends a KEY_ADD message straight to Snapchain with three signatures.

  1. Your custody address signs which key, what it may do, how long it lives, and a nonce.
  2. The requesting app signs a request naming its FID. This client is its own app, so it's your custody address again.
  3. The new key signs the envelope, proving it's really held by whoever added it.

This client's key can post, delete casts, react and follow. It can't add keys or touch the account. It expires after 30 days unused, and every use resets the clock.

Filter by client

Every app key records the FID of the app that asked for it. So your node knows which app every cast came from, without trusting the app.

node fc.mjs apps dwr                 # which apps someone posts from
node fc.mjs feed --only-app <app>    # just that app
node fc.mjs feed --hide-app <app>    # everything but
Under the hood

The server and the node.

Snapchain wants 4 cores, 16 GB of memory, a public IP and ports 3381 to 3383 open. And disk: the snapshot alone is 470 GB compressed, kept while it unpacks. Use 2 TB.

What node.sh adds

It wraps the official installer from farcasterxyz/snapchain. It mounts the data disk and keeps the database there, answers the installer's prompts up front so it works over SSH, and adds fc-node-status to show sync progress. A weekly job keeps the node upgraded.

fc-node-status                          # on the server
curl http://<ip>:3381/v1/info            # from anywhere

Provider notes

Azure. New subscriptions need Compute registered, and the script does it. Free trials allow 4 vCPUs per region, exactly enough. Sizes often sell out, so the script tries others.

AWS. Free-plan accounts can't launch servers this size. Accounts made with the new Projects setup also block EC2 and Lightsail until advanced features are turned on.

Hetzner. The cheapest, once your account is established. New accounts can't create a 2 TB volume yet.

Cost

Billed by the hour.

New accounts usually come with credit, so a day of trying it is often free.

ProviderPer monthPer day
Hetzner, CX43 with 2 TB$176$5.80
AWS Lightsail, 16 GB with 2 TB$289$9.50
Azure, D4s v4 with 2 TB$294$9.70

Troubleshooting

The client can't reach my node.

The API starts after the snapshot is restored. Run fc-node-status on the server, and make sure port 3383 is open.

One shard is months behind and barely moving.

Snapshots are replaced every day, and the node reads the download list for every shard before it starts. If the first sync takes long enough, a shard's snapshot is gone by the time its turn comes, so the node skips it and replays that shard from the beginning, which takes weeks. fc-node-status warns about this and prints the fix: reload-shard.sh --shard N, which fetches today's snapshot for just that shard and swaps it in. It took about an hour for shard 0.

"Nonce is not greater than stored nonce."

Another key was added for your account at the same moment. Run init again.

My cast isn't showing in other apps.

Give it a few seconds. If it never appears, the relay was dropped under load. Post it again.

Go further