create-fc

Your own Farcaster.
Three commands.

Rent a serverA Hetzner Cloud machine with 8 vCPUs, 16 GB of RAM and a 1000 GB disk, billed by the hour. Delete it when you're done. Read more, sync a Snapchain read nodeA full copy of Farcaster's data that follows the network block by block. It serves reads and relays your posts to the validators. Read more, and install a clientfameboy: a feed, posting, and a view of every app that can post as you. Runs on your computer and talks only to your node. Read more. No API keysExcept your own Hetzner API token, which command 0 uses to create the server. Nothing you read or post goes through anyone else's API. How to make one needed! Read + write from your own node.

Do it yourself

Step by step ↓

Rent a server (costs breakdown)~30 seconds

run on your computer
$ curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/provision/hetzner.sh | bash
cx43 is sold out in fsn1 right now; trying the next option

Server fc-node is up at 91.99.76.223 (cx43, 1000 GB volume, nbg1).
It bills by the hour until you delete it; see the end of this message.

Let an agent do it

1 prompt
0

API keys

Your node syncs from the network's own peers and snapshots. Your client only talks to your node.

6

Hours to first sync

6 h 20 min on our last run: under 2 to load the snapshot, then about 4½ to catch up. It runs on its own.

$4

For a one-day test

Hetzner bills by the hour: about $0.16. Delete the server when you're done. Full costs

Quickstart

Copy, paste, wait.

You need

  • A Hetzner Cloud project and an API token with Read & Write access, made at console.hetzner.com
  • The hcloud CLI, logged in: brew install hcloud, then hcloud context create fc-node
  • An SSH key in ~/.ssh (ssh-keygen -t ed25519 if you don't have one)
  • Node.js 22.18 or newer, and git
  • To post: a Farcaster account that already has storage. Read seed phrases before you type one
  1. Create the server

    A cx43 (8 vCPU, 16 GB RAM) with a 1000 GB volume, about $0.16/hour. If cx43 is sold out it tries other locations, then cpx41 (~$0.20/hour), and says which it got. It prints the next two commands with your server's IP filled in.

    curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/provision/hetzner.sh | bash
  2. Set up the node

    Give the server a minute to boot. Setup runs in the background, so you can close the terminal. Check on it with ssh root@<ip> fc-node status.

    ssh root@<ip> 'curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/node/setup.sh | bash'
  3. Install the client

    When fc-node status says "in sync", run this on your own computer. Then fameboy ui opens the client in your browser. Sign in on its first page to see your feed.

    curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/client/install.sh | bash -s -- http://<ip>:3381

Done for now?

Delete the server so it stops billing. It asks before deleting anything.

curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/provision/hetzner-delete.sh | bash

There is no npm package: npm create fc runs an unrelated package someone else published in 2022.

The client: fameboy

See what other clients won't show you.

Who can post as you

Every app you've ever signed in to holds a key that can post as you. See them all, which can sign anything, and when the gasless ones expire.

Your feed, without the big app

Each cast shows the app it came from. Hide the Farcaster app in one click and see what everyone else is building.

Which apps your people use

A breakdown of every client in your feed. In ours, 89% of casts came through one app. Click any other to see only its casts.

Nothing hidden

Every view lists the exact calls it made to your node, ready to paste into a terminal. No API keys, no one else's servers.

fameboy in the browser: Who can post as @dwr. 3 keys, held by @warpcast, @baseapp.base.eth and @neynar, all able to sign anything.
@dwr has 3 app keys. All 3 can sign anything.
fameboy in the browser: Apps in @dwr's feed. 91% of the latest 400 casts came through @warpcast; 9 apps in total.
91% of @dwr's feed comes through one app.

Try it in 60 seconds

No server needed yet: point the client at our demo node, which stays up until 24 October. Then sign in on the page it opens, with an account made for trying things: the client shows your own feed.

curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/client/install.sh | bash -s -- http://91.99.76.223:3381
fameboy ui

Needs Node.js 22.18+ and git. fameboy ui opens the client in your browser; it only listens on your own computer. When you run your own node, re-run the first command with its address.

Tutorial

What each command does, and why.

The quickstart works without reading any of this. Read it when you want to know what's running on your server, what you just signed, or what to do when something breaks. Every output and timing here is from a real run on 18–19 October 2026.

Before you start: Hetzner

Command 0 uses Hetzner Cloud because it bills by the hour and has a clean CLI. Hetzner has no API for creating projects or tokens, so this part happens in the browser:

  1. Sign up at console.hetzner.com. New accounts may need ID or payment verification before they can create servers, which can take a while. Do this first.
  2. Create a project, then Security → API tokens → Generate API token with Read & Write access. Hetzner shows it once.
  3. Run hcloud context create fc-node and paste the token. It's saved in plain text in ~/.config/hcloud/cli.toml. A project used only for this keeps the token's reach small.

New accounts can only have 1024 GB of volumes in total, and Hetzner won't raise that until the account is older: the Limits page says "Your account is too new to request a limit increase." The official Snapchain installer needs about 2 TB, so we load the database differently (see command 1) and fit in 1000 GB.

What it costs (USD, incl. 10% VAT, prices from the Hetzner API)
ItemPer monthPer hour
cx43 server, 8 shared vCPU, 16 GB RAM$20.34$0.033
1000 GB volume$84.37~$0.12–0.14
IPv4 address$0.66$0.001
Total$105.37~$0.16
Total with cpx41, if cx43 is sold out$126.82~$0.19–0.21

Powering a server off doesn't stop the bill. Only deleting it does. This lists what it found, asks you to type fc-node, and deletes the server, volume, firewall and SSH key (34 seconds for us):

curl -fsSL https://raw.githubusercontent.com/eucalyptus-viminalis/create-fc/main/provision/hetzner-delete.sh | bash

The volume holds the database, so the next node re-syncs from scratch. Your app key in ~/.fameboy survives; point the client at the new node by running command 2 again with the new IP.

Command 0: the server

provision/hetzner.sh creates four things, each named so a re-run finds and reuses it: an SSH key, a firewall, a volume, and the server. The volume comes first, so it's attached when the server first boots and formats it at /srv/snapchain.

The firewall opens exactly what a read node needs:

PortProtocolFor
80, 443TCPfc-images: the images people attach to casts, served over HTTPS (80 is how it gets its certificate).
3381TCPHTTP API. Your client talks to this.
3382UDPGossip with other nodes (libp2p over QUIC). A TCP-only rule leaves the node unable to peer.
3383TCPgRPC API, the same data as 3381 for other tools
3390TCPfc-companion: node uptime, who's online, and phone sign-in for friends sharing your node (how).
22TCPSSH
3000TCPClosed. The node ships Grafana with the login admin / admin.

Server types sell out. When Hetzner has no cx43 free in a location, it answers error during placement (resource_unavailable) and creates nothing. We watched cx43 disappear from all three EU locations within a minute, then come back in Nuremberg five minutes later. The script handles it: cx43 in fsn1, nbg1 and hel1, then cpx41 (the same 8 vCPU and 16 GB at about twice the server price) in the same order. It creates the server first and the volume after, in the server's location, because a volume can't move between locations. Pin your choice with TYPE=cx43 or LOCATION=hel1.

If the volume step fails with volumes size limit exceeded (resource_limit_exceeded), your account's volume cap is lower than VOLUME_GB. Check Limits in the console. Other sizes and locations can be set with environment variables: … | TYPE=ccx23 LOCATION=nbg1 bash.

Command 1: the read node

Snapchain is the network that stores Farcaster's messages: every cast, like, follow and profile change. Its official installer, snapchain.sh, runs the node in Docker. node/setup.sh wraps it, starts it as a systemd job so you can disconnect, and changes one thing: how the database gets loaded.

Loading the snapshot

A new node doesn't replay Farcaster from the beginning. It downloads a daily snapshot of each shard (about 397 GB compressed, in ~70 MB pieces) and then fetches the blocks made since. The node's own loader keeps every downloaded piece and an uncompressed copy of each shard until all three are unpacked. We estimate that peaks around 1.5 TB, which is why the docs ask for 2 TB.

fc-load-snapshot streams instead: download a piece, unzip it straight into the database, delete it, repeat. The node only downloads a snapshot when its database folder is empty, so afterwards it boots straight into catching up.

Measured on a cx43 with a 1000 GB volume
ShardPiecesTimeDisk used after
0 (the block shard)37010 min37 GB
12,67163 min297 GB
22,64863 min556 GB

Disk use peaked at 556 GB, the size of the finished database.

Catching up

Our snapshot was 22 hours old by the time it finished loading. The node fetched the missing blocks at about 5 per second per shard and closed the gap by about 4 seconds every second, so it was in sync roughly 5½ hours later. On a second, timed run the snapshot was 19 hours old and the node caught up in 4 hours 25 minutes: 6 h 20 min from command 0 to in sync. The CPU sat 94% idle: the limit is fetching blocks from peers one after another, not the server.

$ ssh root@<ip> fc-node status
catching up: 18.9 h behind, gaining about 4 s per second (~4.7 h to go).

$ ssh root@<ip> fc-node status
  shard 0: 1s behind
  shard 1: 1s behind
  shard 2: 1s behind
in sync. Run command 2 on your own computer.

The first is our demo node right after loading; the second is the same node a few hours later. During setup, fc-node status shows the snapshot's progress instead. On your own computer, fameboy status reports the same numbers.

"Behind" is blockDelay from /v1/info: how many seconds each shard's newest block trails the clock. A synced node stays in single digits. In the same response, mempoolSize: 4294967295 is normal on a read node. It means "not reported", not an overflow.

Two things look alarming and aren't. Failed to dial peer … HandshakeTimedOut for a couple of the bootstrap addresses, and a steady trickle of InsufficientPeers ("read-node-peers"). Our node logged both all day and synced fine.

Why not the official one-liner? snapchain-bootstrap.sh reads its prompts from /dev/tty, which doesn't exist over ssh host 'curl … | bash' or in a systemd job. setup.sh answers both prompts in .env ahead of time and runs snapchain.sh upgrade directly.

Command 2: the client

install.sh clones this repo into ~/.fameboy/app, installs three dependencies (@farcaster/core, viem, @scure/bip39), saves your node's address in ~/.fameboy/config.json, and puts fameboy in ~/.local/bin. Node.js runs the TypeScript directly, with no build step.

The browser client

fameboy ui starts a small server on your own computer (127.0.0.1:4747, or the next free port) and opens it in your browser. Your app key stays in that server; the page never sees it. It shows your own account, so it opens on a sign-in page. Approve in the Farcaster app: scan a QR code and approve on your phone, so your seed phrase never leaves it. The request comes from an app account, the account the phone shows as "@name wants to post on your behalf". Set it up once on your server with ssh -t root@<ip> fc-node app-account, and everyone using your node can sign in this way; give each friend an invite code with fc-node invite <name> (sharing your node). Or set one up for this computer alone with fameboy app-account. If it has no username, setup suggests a free @fameboy<color> with a random color, like @fameboyteal, trying another color whenever one is taken; keep it or type your own. Setup also offers a display name, picture and bio for it, so the approval screen looks like an app, not a bare number. Use your seed phrase: the same steps as fameboy login, offered only because the server listens on your computer alone. Sign out in the header deletes the app key from this computer.

fameboy's feed view: a compose box, @dwr's feed from the 79 accounts they follow, and a row of app chips with counts: @warpcast 364, @fireflyapp 9, @cassie 6 and more.
Feed. Every cast carries a “via @app” label, and the chips above the feed show which apps it came from. Click a chip to hide that app, or “Hide the Farcaster app” to see everything else.
Who can post as @dwr: 3 keys held by @warpcast, @baseapp.base.eth and @neynar, all able to sign anything.
Who can post as you. Every app key on an account, grouped by the app that asked for it. Old onchain keys can sign anything; gasless ones list what they're allowed to do and when they expire. See every app that can post as you.
Apps in @dwr's feed: 91% of the latest 400 casts came through @warpcast, 9 apps in total, as a bar chart.
Apps in your feed. The same data as the chips, as a chart. Click a bar to see only that app's casts.
Your node view: address, Snapchain 0.14.3, in sync, three shards 0–4 s behind, and a table of every request the page made, with times and a Copy curl button.
Your node. Sync status, and every request the page made to your node, each with a Copy curl button so you can run it yourself.

Reading from the terminal

Every view above is a handful of plain GETs to your node, so you can repeat any of them with curl:

$ fameboy casts pinkmink -n 3
@pinkmink · 1h · via @pinkmink  0xac0dd681
clear

@pinkmink · 17h · via @pinkmink  0x39d6294b
posting through a fresh create-fc node, three commands later

@pinkmink · 1d · via @pinkmink  0x085bf8a4
hello from my own snapchain read node 👋

$ curl "http://<ip>:3381/v1/castsByFid?fid=3355372&reverse=true&pageSize=3"

Snapchain has no "home feed" endpoint. A feed is something a client builds. Ours is the simplest version: ask the node who you follow, fetch the latest casts from each, and merge them by time. That's one request per account you follow. For an account following 1,041 people, the feed took 13.5 seconds from Sydney to our node in Germany, while the same 1,041 requests made on the server itself took 1.7 seconds. Running the client close to the node pays off.

One oddity: a few old casts carry timestamps decades in the future (we found one dated 2061). They would sit at the top of any time-sorted feed forever, so fameboy feed skips anything more than 10 minutes ahead.

Posting

fameboy login asks for a seed phrase once, uses it to authorize an app key for this client, and then forgets it. Read Seed phrases before you run it. Here's a real run:

seed phrase (hidden): ************ words
  m/44'/60'/0'/0/0  0x97547ffbA3F3e5a7553aA36948F618f39647F2Ea  fid 3355372
storage: 1 unit(s)
app key: 0xaa2778f5b991db794630e34a0e192ce1633c135786ecbadf253406938ebdcf8e
KEY_ADD: 0x23c71047ebe9209753273ca2b33c931989ddad97, nonce 2, ttl 30 days
submitted to your node, which relays it to the validators. Waiting for it to land...
done. App key saved to /Users/sdf/.fameboy/appkey.json (mode 600). Your seed phrase was not saved.

Then the usual verbs. Each one is signed with the app key, sent to your node, and read back from it until it shows up:

Our first writes through our own read node, time until each was readable again
CommandConfirmed after
fameboy cast "hello from my own snapchain read node 👋"1.8 s
fameboy like pinkmink/0x085bf8a4 · fameboy recast …4.0 s each
fameboy unlike … · fameboy unrecast …4.0–4.1 s
fameboy follow 13642 · fameboy unfollow 136426.3 s · 1.8 s

@names in a cast become real mentions, --reply user/hash replies, --embed url attaches a link, and --dry-run checks the message with your node without sending it.

A username

fameboy username alice claims a name and sets it. It's the one step that uses a service other than your node: the fname server at fnames.farcaster.xyz, run by Farcaster, free, no key. Your custody key signs the claim, the fname server countersigns, the proof reaches the network and then your node, and only then will the node accept a message setting your username.

Filtering by the app that posted

Every cast is signed by an app key, and every app key records which app asked for it. So fameboy labels each cast with its app, and filters on it:

$ fameboy apps dwr
   1 key   @warpcast (fid 9152)
   1 key   @baseapp.base.eth (fid 309857)
   1 key   @neynar (fid 6131)

$ fameboy feed me --not-app warpcast
$ fameboy feed me --app fireflyapp --app 9152

In one account's feed of 16,416 recent casts, 89% came through the Farcaster app (fid 9152, still called @warpcast). Excluding it is a quick way to see what everything else looks like.

Hosting it for others

Most people won't run a client. Once your node has an app account, ssh root@<ip> fc-node client hosts fameboy on it at a web address named after that account, like https://fameboyteal.<ip with dashes>.sslip.io. Anyone can open it, approve on their phone, and use Farcaster through your node.

Their app key is made in their browser and never leaves it. Every cast, like and follow is signed there, and your server keeps nothing about the people who use it. A copy named @fameboy<color> takes that colour, in four shades like a Game Boy Color screen. The hosting guide covers your own domain, invite-only sign-in, and what people trust you with.

Concepts

The pieces you just used.

FID
A Farcaster account is a number, its FID, registered on OP Mainnet (Optimism). Usernames, keys and storage all hang off it. The account we tested with is 3355372.
Custody address
The Ethereum address that owns the FID, derived from the seed phrase. Most wallets use the first address, m/44'/60'/0'/0/0; fameboy login checks that one first, then the next nine. It can transfer the account and add or remove keys. It never needs to sign a cast.
App keys (signers)
Ed25519 keys that sign everyday messages for one app. The custody key authorizes each one, naming the app that asked (its requestFid). One account can have many: Farcaster's app, Firefly, this client.
App account
The account that asks for an app key: its custody key signs the request, and its name is what you approve on your phone and what your casts show "via". There's no shared app account, since every copy would need its key, so each one names its own, like @fameboyteal: setup suggests a free @fameboy<color>.
Gasless app keys
App keys used to be registered with an Optimism transaction. Now a KEY_ADD message does it: no transaction, no gas, about a second. Each key lists the message types it may sign (its scopes) and expires after 30–90 days unused.
Snapchain messages
Casts, reactions, follows and profile fields are signed protobuf messages. Timestamps count seconds from 1 January 2021, not 1970. A message's ID is a 20-byte BLAKE3 hash, the 0x085bf8a4… you see in fameboy.
Shards
Snapchain splits users across shards 1 and 2 by a hash of the FID. Shard 0 orders the other two and holds things every shard needs, like app keys. Each shard is its own chain, so their heights differ.
Read nodes vs validators
Validators propose and vote on blocks; there's a fixed set of them. A read node, like yours, fetches the decided blocks, checks their signatures, and serves the API. Anyone can run one.
Mempool and submission
Your read node accepts writes: it checks the message, gossips it to the validators, and keeps nothing. "Accepted" means "valid and relayed", not "in a block". That's why fameboy reads every write back. If gossip drops it, send it again.
Storage rent
An account needs rented storage units to post, paid on Optimism. One unit comes with registration (about $0.20 when we checked). When a store fills up, the oldest messages are pruned. fameboy login checks there's at least one unit.
fnames
Usernames like @dwr come from the fname server, which signs a proof that the network stores. ENS names (.eth) work too.

Seed phrases

Treat it like rm -rf.

A seed phrase is the master key to an account. Whoever has it can post as you, move your FID to themselves, and take any funds at the custody address. Like rm -rf, it's a normal tool that's unforgiving when used carelessly.

No account yet?

fameboy new-account registers a real account for about $0.20 of ETH on Optimism, and shows its phrase once. Add --recovery with an address you control.

Read before you paste

Only give a seed phrase to software from developers you trust, after reading what it does with it. For this client that's client/src/keys.ts: in memory, one signature, never written or sent.

Prefer delegated signing

The better pattern: the app shows a request, your wallet or the Farcaster app approves the key, and the phrase never leaves it. In fameboy that's approve on your phone. If you type a phrase, do it on your own computer.

Not through an AI's shell

We learned this one live: run through a coding assistant's shell, the account script's output, phrase included, landed in the chat transcript. Run phrase commands in your own terminal.

When it breaks

Errors we actually hit.

volumes size limit exceeded

Your account's total volume cap is below the requested size. New accounts get 1024 GB and can't ask for more yet. Keep VOLUME_GB at 1000 or less.

error during placement (resource_unavailable)

That server type is sold out in that location. Command 0 moves on by itself, ending with cpx41. If you pinned TYPE or LOCATION, try another, or wait a few minutes.

fameboy prints its help instead of running

A pasted command can carry invisible characters. fameboy now says unknown command "username​" (it contains hidden characters…). Retype the command.

fameboy login spends minutes on "checking indexes 1-9"

The phrase's first address owns no account, and your node scans all 3.3 million registrations before answering "not found", 30–40 s each time. Check it's the right phrase.

"not visible on your node yet" after a write

Read nodes relay and forget. If the gossip was lost, the message is gone: run the command again. We haven't lost one yet.

fc-node status: "isn't answering and setup isn't running"

Setup stopped partway. Run command 1 again: finished shards are skipped and a partial one is redone.

Help me test it

Run it, then tell me what broke.

I'm entering this into Balaji's Farcaster client prize, and it only counts if other people run it. If you do, I'd love to hear how it went, good or bad.

Post how it went

Your server's on me

The first 5 people who run the three commands get $5 USDC to cover the server, whatever you think of it. Send your node's IP to @3070.eth.

Do it together

Stuck, or just want company? Message @3070.eth for a 30-minute session and we'll run it on your machine together.

Run by

Nobody but me yet. Be the first, and your name goes here.

create-fc

Your node.
Your client.

What you get

  • A Snapchain read node that syncs unattended
  • Reads, casts, follows, likes and recasts through it
  • A gasless app key, scoped and self-expiring
  • A browser client that shows who can post as you, and which apps your feed comes from
View on GitHub

MIT licensed. Not affiliated with Farcaster or Merkle Manufactory.