An optional slice: wallet sign-in, chain reads, token swaps, and presenting a token your workspace
owns. **It is not installed by default** -- see [Getting it](#getting-it) below, which is one
command.

Once installed it is still **off in the sense that matters**. With no RPC url configured, the
adapters return deterministic offline fakes and nothing reaches a network, so the whole surface
renders and the entire test suite runs with zero external accounts.

Open `/onchain` in your running app. That page reports what is actually configured on *your*
install, not what the docs wish were configured.

## Getting it

**The module is not installed by default.** The kit ships it staged, in `optional/onchain/`, so an
app that is not onchain carries no crypto code, no crypto routes, and no Node toolchain at all.
Adding it is one command:

```bash
script/onchain install    # copies the files, applies the wiring, migrates, builds the island
script/onchain status     # staged, or installed
script/onchain remove     # the exact inverse
```

Install copies about 138 files to the paths they belong at, applies a small patch to thirteen
shared files (the gem that runs the bundle step, the Node stage in the `Dockerfile`, the account
associations, the token entitlement strategies), creates four tables, and builds the island. Remove
reverses all of it and `script/verify-shape.sh onchain` proves the tree is clean afterwards.

Node is needed only once the module is installed: the browser half is a React island bundled with
esbuild. Everything else in the kit stays importmap + Propshaft with no build step.

## The one rule

**The server owns the message and the calldata.**

- Rails builds the Sign-In With Ethereum message, because Rails verifies it.
- Rails calls the swap aggregator, because Rails holds that key.
- Rails reads the chain, so the RPC key never reaches a browser.
- The browser signs what it is handed, sends what it is handed, and reports the hash back.

Everything else in this module follows from that sentence. It is enforced rather than remembered:
`spec/onchain/no_client_side_chain_logic_spec.rb` greps the browser code for calldata encoding,
unit conversion, message construction, `Number()` on uint256 values, and direct chain reads.

Two consequences worth understanding:

**`connect-src` stays `'self'` for reads.** Because every read goes through `/onchain/rpc` on your
own origin, the Content Security Policy does not need to name an RPC provider, whatever provider
you configure. That endpoint's method allowlist *is* its security model: without it, it is an open
relay anyone on the internet can point at your provider account.

**A visitor's address is never handed to a third party** on page load, which is what would happen if
the browser talked to an RPC provider directly.

## What it costs

Adding this module is the only reason this kit needs Node. `bin/dev` runs an esbuild watcher
alongside Rails, and the browser bundle is real:

| Bundle | Gzipped | When it loads |
|---|---|---|
| `external` | ~382 KB | Any page with the wallet island |
| `identity/Basename` | ~468 KB | Only when an ENS or Basename is rendered |
| `privy` | ~865 KB | Only if you switch to embedded wallets |

`bin/js-budget` prints these on every `bin/check` and fails if one exceeds its ceiling, so a
dependency that doubles the bundle is a red build rather than something nobody notices.

If you install it and change your mind, `script/onchain remove` is the exact inverse, and
`script/verify-shape.sh onchain` proves the tree is clean afterwards.

## Where to go next

- [Wallet Sign-In](/docs/onchain/wallet-sign-in): how SIWE works here and why the cold-signup flow has two steps.
- [Chains and Reads](/docs/onchain/chains-and-reads): the Chain adapter, the RPC proxy, and the offline fake.
- [Swaps](/docs/onchain/swaps): quoting through 0x, and the fake that deliberately cannot be signed.
- [Token Offerings](/docs/onchain/token-offerings): registering a token, and why deploying one is not built.
- [Token Gating](/docs/onchain/token-gating): using a token balance instead of a subscription.
- [Extending It](/docs/onchain/extending-it): adding an onchain action without breaking the boundary.
