# Base Sepolia developer-preview runbook

> **Operator-gated, like `docs/deploy/demo.md`.** This is the full sequence —
> deploy, wire, verify, ship — for standing up a **free, reversible developer
> preview** of the whole funnel on Base Sepolia. Nothing here should run
> unattended; several steps broadcast from a real (throwaway) key.

## 1. What this is

A developer preview is the whole self-serve funnel — the browser demo, the
`register.html` on-chain registration flow, an SDK resolving
`Registry.canonical()`, and the onboarding manifest an agent discovers via a
401 — **live on Base Sepolia (chainId `84532`)**, at zero cost and fully
reversible. It proves the product end to end against a real chain before
anything commits to mainnet.

This is **not** mainnet launch. `Registry.canonical()`'s compiled-in map stays
empty for chain `8453` (Base mainnet) until the launch runbook's Phase 3
mainnet deploy — see `docs/strategy/2026-08-01-launch-runbook.md`. The preview
only ever touches `84532`, and wiring it in is a one-command, one-commit,
one-revert operation (§4, §6).

The preview is three layers, each already built and each documented
separately — this runbook is the missing glue that sequences them:

1. **The verifiable browser demo** (`docs/deploy/demo.md`) — deploys the
   registry + two demo tenants and ships `landing/demo.html`, a real
   passkey-signed deed verified client-side against a real chain read.
2. **The register/SDK/manifest funnel** (`just preview-wire`, new in this
   preview) — wires that same registry address into the compiled-in canonical
   map and regenerates `landing/registry.json` +
   `landing/.well-known/grantor-onboard.json`, so `register.html` resolves a
   real tier table and `Registry.canonical()` works against `84532` for any
   consumer, not just the demo page.
3. **The onboarding manifest** — already live at
   `/.well-known/grantor-onboard.json` (`docs/ONBOARD.md`); step 2 is what
   flips its `chains` entry for `84532` from absent to real.

## 2. Prerequisites (operator)

- Everything `docs/deploy/demo.md` §0 requires: Foundry (`forge`, `cast`,
  `anvil`), `jq`, Node, and `npm exec --yes --package wrangler` reachable
  (Cloudflare Pages deploy, §7).
- A **deploy keypair**, generated and held with the same hygiene as any other
  operator key in this repo (Tails session recommended; never paste a private
  key into chat or a committed file). `docs/deploy/demo.md` §1
  (`cast wallet new`) is the exact recipe — this key becomes the deployer,
  the funded demo tenant's admin, and the origin-vouch signer.
- **Base Sepolia faucet ETH** for that key — a few cents' worth covers the
  whole deploy (MockUSDC is minted free; only gas costs anything). Pick one
  of the faucets named in `docs/deploy/demo.md` §1 (Coinbase Developer
  Platform, Alchemy, thirdweb).
- A **throwaway `TREASURY` address** — `contracts/script/DeployTestnet.s.sol`'s
  `_resolveTreasury` **requires `TREASURY` to be set explicitly on any chain
  other than anvil/ganache**, and `84532` is real Base Sepolia, so it will not
  silently default here. The treasury address is otherwise meaningless for a
  preview deploy (draws just accumulate testnet MockUSDC nobody redeems) — the
  deployer address itself is fine; do not reuse a mainnet treasury candidate
  (`docs/deploy/demo.md` §2).

## 3. Deploy the registry + demo

Run the demo's own deploy generator against Base Sepolia:

```bash
RPC=https://sepolia.base.org \
  DEPLOYER=0x<your funded throwaway private key> \
  TREASURY=0x<your throwaway treasury address> \
  ORIGIN=<the origin you will serve landing/ from> \
  AUDIENCE=<same as ORIGIN> \
  CHAIN_ID=84532 \
  EXPLORER_BASE=https://sepolia.basescan.org \
  OUT=landing/demo/demo-config.json \
  just demo-deploy
```

This is `docs/deploy/demo.md` §3, verbatim — read that runbook for the full
detail (what the script does, the printed `GrantorRegistry=0x...` address to
save, the "every run deploys a brand-new registry" warning, and the local-fork
rehearsal in its closing section). The short version: it deploys `MockUSDC` +
a fresh `GrantorRegistry`, stands up tenant `1` funded to `Active` and tenant
`2` created-but-unfunded (`Inactive`), signs the origin vouch for `ORIGIN`,
and writes `landing/demo/demo-config.json`.

Save the `GrantorRegistry` address printed at the end (also recoverable from
`landing/demo/demo-config.json`'s `registry` field) — the next step needs it.

## 4. Wire the register/SDK/manifest side

```bash
just preview-wire <GrantorRegistry addr from step 3>
```

This is Task 1's recipe (`justfile`, near `onboard-gen`): it writes
`(84532, "<addr>")` into `CANONICAL_REGISTRIES` in
`crates/grantor-sdk-core/src/registry.rs` (which stays `&[]` — empty — until
an operator runs this), then runs `just onboard-gen` to regenerate
`landing/registry.json` and `landing/.well-known/grantor-onboard.json` from
the now-populated map. Review before committing:

```bash
git diff crates/grantor-sdk-core/src/registry.rs landing/registry.json \
  landing/.well-known/grantor-onboard.json
```

Both `landing/registry.json` and the onboarding manifest should now carry a
`"84532"` entry pointing at the deployed registry. Then:

```bash
TZ=UTC git commit
```

(the pre-commit hook refuses a non-UTC-timezone commit). After this commit,
`register.html` resolves a real tier table on `84532`,
`Registry.canonical()` returns the deployed address for any SDK consumer
targeting Base Sepolia, and any agent that discovers the onboarding manifest
sees a live chain entry instead of an absent one.

## 5. Verify

- Serve `landing/` locally (`cd landing && python3 -m http.server 8000`) and
  open `register.html` with a wallet connected to Base Sepolia — it should
  resolve a real registry address and tier table instead of the "this chain
  has no registry" honesty-gate message (`landing/register.js`).
- The demo (`docs/deploy/demo.md` §6, once deployed to Pages per §7 below)
  reads green: "Verify a real deed" mints and verifies against the funded
  tenant; "See the billing gate reject an unpaid tenant" is refused against
  the unfunded one.
- Confirm the manifest carries the chain entry:

  ```bash
  curl -s https://<your served origin>/.well-known/grantor-onboard.json \
    | jq '.chains."84532"'
  ```

  (or, before it's deployed anywhere, read the local file:
  `jq '.chains."84532"' landing/.well-known/grantor-onboard.json`).

## 6. Local-anvil rehearsal (free, before touching real Sepolia)

Rehearse the entire sequence — deploy generator *and* the wiring recipe —
against a local fork of Base Sepolia, with no faucet and no real funds. This
mirrors `docs/deploy/demo.md`'s own closing "Fork-testing this runbook"
section, extended one step further to cover `preview-wire`:

```bash
anvil --fork-url https://sepolia.base.org > /tmp/fork.log 2>&1 &
sleep 4
RPC=http://localhost:8545 \
  DEPLOYER=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
  TREASURY=0x000000000000000000000000000000000000dEaD \
  ORIGIN=http://localhost:8000 AUDIENCE=http://localhost:8000 \
  CHAIN_ID=84532 EXPLORER_BASE=https://sepolia.basescan.org \
  OUT=/tmp/demo-config.fork.json \
  just demo-deploy
export REG=$(jq -r .registry /tmp/demo-config.fork.json)
just preview-wire "$REG"
git diff --stat crates/grantor-sdk-core/src/registry.rs landing/registry.json \
  landing/.well-known/grantor-onboard.json   # should show the 84532 entry
kill %1
```

Then **revert** — this is a rehearsal, not the real deploy, and the map must
stay empty until a real Base Sepolia address exists:

```bash
git checkout -- crates/grantor-sdk-core/src/registry.rs landing/registry.json \
  landing/.well-known/grantor-onboard.json
```

Do this revert either entirely before making any other edits, or entirely
after any other pending edits are already committed — `git checkout --`
restores those three files to `HEAD`, which clobbers any uncommitted work on
them too (the same lesson Task 1's own rehearsal recorded).

## 7. Then (separate step): ship the landing page

Deploying `landing/` to Cloudflare Pages and attaching a real domain is its
own follow-up, not part of wiring the registry:

- `docs/deploy/demo.md` §5 (`wrangler pages deploy` / `just landing-deploy`)
  is the deploy mechanism, unchanged by this preview.
- Attaching `chaingrantor.com` and deciding whether to remove the `noindex`
  line in `landing/_headers` are launch-runbook items
  (`docs/strategy/2026-08-01-launch-runbook.md`, Phase 0 and Phase 4) — do
  **not** remove `noindex` for a developer preview; that flag is reserved for
  the real launch decision.

## 8. Funding upkeep + teardown

- `docs/deploy/demo.md` §8 — `drawPeriod` is permissionless and nothing
  auto-renews; periodically check the funded tenant's status and re-run
  `SetupTenant.s.sol` with a fresh `FUND` amount before it drops out of
  `Grace`, against the **same** registry (do not re-run `demo-deploy`, which
  deploys a brand-new registry and would orphan the address just wired in).
- **Teardown / reversal:** `just preview-wire` is reversible the same way its
  own rehearsal (§6) is — restore the empty map:

  ```bash
  git checkout -- crates/grantor-sdk-core/src/registry.rs landing/registry.json \
    landing/.well-known/grantor-onboard.json
  ```

  (as a committed change, revert with a normal `git revert` of the wiring
  commit instead). The Base Sepolia contracts themselves stay on-chain
  either way — harmless, testnet-only, no real funds — exactly like
  `docs/deploy/testnet.md`'s teardown note.

## Anonymity notes

Same posture as every other deploy runbook in this repo
(`docs/deploy/testnet.md` § Anonymity notes, `docs/deploy/demo.md` §1): fund
the deploy key from a fresh source, never reuse a doxxed wallet, and commit
under the existing `grantor-dev` identity with `TZ=UTC`.
