morphit/docs/SWITCHING-NETWORKS.md

550 lines
20 KiB
Markdown

# Switching networks: testnet, staging, and going live
Plain-language walkthrough for two scenarios:
1. **"I want to test before launching."** Stand up a
throwaway Morphit instance to shake out bugs without
polluting your eventual production account's reputation.
2. **"I'm ready to go live."** Wipe the staging instance
and switch to your real production account on Blurt
mainnet.
Designed for a working sysadmin who is comfortable with
`systemctl`, `psql`, and editing config files — but isn't
expected to know anything about Blurt internals. If you've
never run Morphit before, read
[`docs/RUN-A-MORPHIT-NODE.md`](RUN-A-MORPHIT-NODE.md) first
for the bigger picture.
---
## What you're going to do — staging on Blurt mainnet
The recommended pre-launch testing pattern is **staging on
mainnet**: a second Morphit instance pointing at the same
Blurt chain you'll launch on, but with:
- A separate Blurt account (e.g. `acmecorp-staging` if your
production account will be `acmecorp`)
- A separate Postgres database (`morphit_staging` next to
`morphit_indexer`)
- A separate config directory (`/opt/morphit-staging/` next
to `/opt/morphit/`)
- A small budget of real BLURT (~50 BLURT, ≈$5) for test
ops (listing fees, chat fees, etc.)
This is what every running Blurt-adjacent project does for
staging. The staging traffic doesn't pollute your eventual
production account's reputation history because they're
different accounts on the same chain.
**What this catches before launch:**
- Postgres permission setup and role creation
- systemd unit configuration and reboot survival
- nginx / TLS / reverse-proxy config (or Caddy if you chose it)
- The wizard end-to-end (`morphit-ops init`)
- Backup-and-restore loop (test the restore!)
- Federation discovery (other Morphit operators see your
staging instance via on-chain `morphit_operator_register_v1`)
- `morphit-ops edit` workflow for post-launch tunables
**What it costs:** ~30 minutes of operator time + ~$5 of
BLURT. Test ops on mainnet cost real BLURT but small enough
to be a non-event for a serious operator.
The "going live" workflow then drops the staging DB, creates
a fresh production DB, and re-runs the wizard with
production credentials. Same chain, so no chain-id change
is needed; the wizard ships mainnet chain_id by default.
> [!NOTE]
> A community-maintained Blurt testnet exists at
> `https://testnet-rpc.beblurt.com`, but Morphit currently
> can't talk to it without code changes (mainnet asset
> symbol and address prefix are hardcoded in ~7 places).
> See the appendix at the bottom of this doc for the full
> story. For pre-launch testing, staging-on-mainnet
> covers everything you actually need.
---
## Procedure 1: Stand up a staging instance
Roughly 30 minutes of operator time.
### Step 1.1 — Create a separate Blurt account for staging
You need a Blurt account that's distinct from your eventual
production account. Naming convention: if your production
account will be `acmecorp`, name your staging account
`acmecorp-staging` or `acmecorp-test`.
Go to any Blurt frontend (blurt.blog, beblurt.com) and create
the account. Save the master password. Generate the four
keys (owner, active, posting, memo) — Morphit uses the active
key to broadcast ops.
Send the new account ~50 BLURT for test ops. Listing fees,
chat fees, etc. will be charged against this balance.
### Step 1.2 — Create a separate Postgres database for staging
On the box that will run the staging Morphit:
```sh
sudo -u postgres psql -c "CREATE ROLE morphit_staging LOGIN PASSWORD 'pick-a-strong-one';"
sudo -u postgres psql -c "CREATE DATABASE morphit_staging OWNER morphit_staging ENCODING 'UTF8' LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;"
```
This is identical to the production setup but with `_staging`
suffix on both the role and the database.
### Step 1.3 — Run the wizard with --out pointing at a staging directory
The wizard generates `morphit.config.env` and `morphit.env`
in the repo root by default. Use `--out` to put them
somewhere else so production config isn't overwritten:
```sh
mkdir -p /opt/morphit-staging
cd /opt/morphit
npm exec --workspace apps/ops-cli morphit-ops -- init --out /opt/morphit-staging
```
When the wizard asks:
- **Database URL:** use the staging connection string
(`postgres://morphit_staging:PASSWORD@localhost/morphit_staging`)
- **Relay account:** the staging account from Step 1.1
- **Posting key:** the staging account's active key (paste it
in; the wizard will encrypt it)
- **Origin URL:** if you haven't decided on the staging URL
yet, leave this blank; you can set it via `morphit-ops edit`
later
The wizard ships **mainnet chain_id** as the default. This
is correct for staging-on-mainnet — staging instances point
at the same chain as production, just with a different
account and database.
### Step 1.4 — Run the indexer + relay against the staging config
Two ways:
**A. Test it interactively first** (good for the first run):
```sh
cd /opt/morphit
set -a; . /opt/morphit-staging/morphit.env; set +a
npm start --workspace apps/indexer
```
In another terminal:
```sh
cd /opt/morphit
set -a; . /opt/morphit-staging/morphit.env; set +a
npm start --workspace apps/relay
```
You should see the indexer log "starting" with the
chain_id_prefix matching mainnet, then "block X applied"
messages every few seconds.
**B. Set up systemd units for staging** (once you're happy
the manual run works):
Copy `ops/systemd/morphit-indexer.service` to
`/etc/systemd/system/morphit-staging-indexer.service` and
edit:
- Change the `Description=` to reference staging
- Change `EnvironmentFile=` to point at
`/opt/morphit-staging/morphit.env`
- Change `WorkingDirectory=` if you want logs separated
Same for the relay unit. `systemctl daemon-reload` and start.
### Step 1.5 — Use the staging instance
Hit your staging origin in a browser, post a few test orders,
exchange chat messages with another account you control,
verify reputation flow. Watch
`journalctl -u morphit-staging-indexer -f` for any errors.
When you find bugs (you will), fix them in the codebase, then
on the staging box: `git pull`, run migrations, restart
services. Same workflow as production maintenance.
### Step 1.6 — Move on when you're satisfied
Staging gives you a real-data sandbox. Use it until you've
seen:
- Account creation works end-to-end
- Order posting + listing fee verification works
- Chat between two accounts works
- Feedback flow works
- Backups run successfully (test the restore!)
- Operator-config edits via `morphit-ops edit` work
Once that list is green, you're ready for the production
launch.
---
## Procedure 2: Going live (wipe staging, switch to mainnet for real)
This is the procedure for **destroying the staging database
and starting fresh with a production account on mainnet**.
Roughly 15 minutes if you've already done Procedure 1.
> [!CAUTION]
> This procedure DESTROYS the staging database. Anything in
> it — chat history, indexer state, queued relay transfers
> for the staging account — is gone. This is the intended
> behavior for going live. If you have anything in staging
> you want to keep, back it up to a separate file BEFORE
> running these commands.
### Step 2.1 — Stop the staging services
```sh
sudo systemctl stop morphit-staging-indexer
sudo systemctl stop morphit-staging-relay
```
If you ran the services manually instead of via systemd, just
Ctrl+C the running processes.
### Step 2.2 — Decide: same machine or new machine?
Two paths from here.
**Path A: Repurpose the staging box for production.** Keep
the same hardware, drop the staging DB, create a fresh
production DB on the same Postgres, re-run the wizard with
production credentials, install production systemd units.
**Path B: Leave staging running on its current box, deploy
production on a new box.** Useful if you want to keep
running staging in parallel for ongoing testing post-launch.
**Path A is fine for solo operators.** **Path B is right if
you're going to run a real federation node and want to keep
testing in parallel.** The doc continues with Path A; for
Path B, just do a fresh `morphit-ops init` on the new box
without any of the wipe steps below.
### Step 2.3 — Drop the staging database
```sh
sudo -u postgres psql -c "DROP DATABASE morphit_staging;"
sudo -u postgres psql -c "DROP ROLE morphit_staging;"
```
This is the destructive step. You're explicitly removing the
staging DB. Postgres will refuse the DROP if there are still
active connections — in that case, double-check the services
in Step 2.1 are stopped, then retry.
### Step 2.4 — Create the production database
If you haven't already created the production role + DB
during planning, do it now:
```sh
sudo -u postgres psql -c "CREATE ROLE morphit_indexer LOGIN PASSWORD 'PRODUCTION-PASSWORD-HERE';"
sudo -u postgres psql -c "CREATE DATABASE morphit_indexer OWNER morphit_indexer ENCODING 'UTF8' LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8' TEMPLATE template0;"
```
Use a different password than staging. Save it in your
secrets manager (or wherever your operations runbook says to
keep production credentials).
### Step 2.5 — Move the staging config aside (don't delete it yet)
```sh
mv /opt/morphit-staging /opt/morphit-staging.archived-$(date -u +%Y%m%d)
```
This preserves the config + keystore for one rollback turn,
just in case. You can delete it permanently after a week of
stable production.
### Step 2.6 — Re-run the wizard with production values
```sh
cd /opt/morphit
npm exec --workspace apps/ops-cli morphit-ops -- init
```
(No `--out` flag — this writes to the repo root, which is the
default production path.)
When the wizard asks:
- **Database URL:** the production connection string
(`postgres://morphit_indexer:PRODUCTION-PASSWORD-HERE@localhost/morphit_indexer`)
- **Relay account:** your real production Blurt account
- **Posting key:** the production account's active key
- **Origin URL:** your real public URL
- (everything else: actual production values)
The wizard writes mainnet chain_id automatically. No special
action needed — staging-on-mainnet and production-on-mainnet
both pin to the same chain_id, so this just works.
### Step 2.7 — Install production systemd units
If you used the staging-suffixed unit names earlier, swap to
the regular ones now:
```sh
sudo systemctl disable morphit-staging-indexer morphit-staging-relay
sudo cp /opt/morphit/ops/systemd/morphit-indexer.service /etc/systemd/system/
sudo cp /opt/morphit/ops/systemd/morphit-relay.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now morphit-indexer
sudo systemctl enable --now morphit-relay
```
(Edit the unit files first to point `EnvironmentFile=` at the
real morphit.env path if you're using a non-default
location.)
### Step 2.8 — Watch the first sync happen
```sh
sudo journalctl -u morphit-indexer -f
```
You should see:
- A `starting` line with chain_id_prefix matching mainnet
- A `migrations_applied` line (the schema is built fresh on
the empty production DB)
- Block-applied messages catching up to chain head
The first-run catch-up scans Blurt history for any
`morphit_*` ops on accounts your indexer cares about. On a
fresh-launch instance with no existing Morphit history, this
finishes in seconds.
### Step 2.9 — Smoke-test the production deployment
From a browser pointed at your production origin:
- Hit the homepage; it should load
- Check `/instances` shows your origin in the federation
directory (this can take up to 10 minutes for federation
probes from other operators to update)
- Sign up with a fresh test account and post an order
- Verify the order appears in the public orderbook
If anything breaks, check `journalctl` for the relevant
service. Common first-time issues:
- **Origin URL mismatch:** your reverse proxy and
`MORPHIT_INSTANCE_ORIGIN` must agree, or the federation
directory will reject the operator-register op
- **Insufficient relay BLURT:** see
[`OPERATIONS.md §1`](OPERATIONS.md) for the auto-top-up
setup
- **Migration failure:** rare, but check the schema-vN.sql
files in `apps/indexer/src/db/` are all present in the
deployed checkout
### Step 2.10 — You're live
The federation will discover your instance within ~10 minutes
via the operator-register op. At that point users on other
Morphit instances will see yours in their `/instances`
directory and can pick it for chat or trading.
After a week of stable production, delete the archived
staging config:
```sh
sudo rm -rf /opt/morphit-staging.archived-*
```
And you're done.
---
## Quick reference: the difference between staging and production
| | Staging | Production |
|---|---|---|
| Blurt account | `you-staging` | `you` |
| Postgres role + database | `morphit_staging` / `morphit_staging` | `morphit_indexer` / `morphit_indexer` |
| Repo config files at | `/opt/morphit-staging/morphit*.env` | `/opt/morphit/morphit*.env` |
| systemd units | `morphit-staging-{indexer,relay}.service` | `morphit-{indexer,relay}.service` |
| Origin URL | something throwaway (e.g. staging subdomain) | your real URL |
| Federation directory listing | hidden (no operator-register op) | public (operator-register op broadcast) |
| Chain | Blurt mainnet | Blurt mainnet (same) |
| Chain ID | same as production | same as staging |
The chain is the same. Everything else is isolated.
## What about the frontend?
The frontend (apps/web) builds against `DEFAULT_RPC_ENDPOINTS`
in `apps/web/src/lib/net/config.ts`, which is a hardcoded
list of Blurt mainnet RPC nodes. Users can override
per-browser via Settings → Endpoints.
For staging-on-mainnet, no change is needed — the frontend
hits the same mainnet RPCs as the indexer, and the indexer's
own data is still isolated to your staging DB.
(For testnet, the frontend would also need code changes —
see the appendix at the bottom.)
## What about backup + rollback?
Backups belong on a different schedule and survive the
testnet-to-mainnet switch unchanged — see
[`OPERATIONS.md`](OPERATIONS.md) §1 for the canonical backup
procedure.
If launch goes wrong and you need to rollback to staging:
1. Stop production services
2. Restore the staging DB from your archived backup
3. Move `morphit-staging.archived-*` back to `morphit-staging/`
4. Re-enable the staging systemd units
In practice nobody does this — by the time you're at Step 2.7
you've already validated the deployment in Procedure 1. But
the fact that you CAN rollback is what makes the wipe step in
2.3 feel safe.
## Common questions
**Q: Can I just edit `MORPHIT_INDEXER_CHAIN_ID` in
`morphit.env` instead of wiping the DB?**
No. The indexer pins chain_id at first run and refuses to
boot on mismatch — exactly so you can't accidentally cross-
contaminate. The only correct way to "switch chains" is to
wipe the DB and start over. This is a feature, not a
limitation.
**Q: Do I need a separate frontend deployment for staging?**
Only if you want a separate URL. The frontend bundle is
stateless — it doesn't know whether the indexer it's talking
to has staging or production data. Many operators run a
single frontend bundle and serve it from both staging and
production origins.
**Q: My staging instance broke and I don't want to debug it.
Can I just blow it away?**
Yes — repeat Procedure 2 substituting `morphit_staging` for
`morphit_indexer` in Step 2.3, then re-run Procedure 1 from
the top. Staging is meant to be cheap to recreate.
**Q: Will my staging account's reputation carry over to
production?**
No, because they're different accounts. Reputation in
Morphit is per-Blurt-account, and your staging account
(`you-staging`) and production account (`you`) are entirely
unrelated identities to the chain. This is the right
property — staging traffic SHOULD NOT pollute production
reputation.
**Q: I'm seeing "chain_id mismatch" errors at boot. What
does that mean?**
This is the indexer refusing to boot because the chain_id in
your `morphit.env` doesn't match the chain_id recorded in the
indexer_state table. Either (a) you're trying to switch
chains without wiping the DB — see "Q: Can I just edit
MORPHIT_INDEXER_CHAIN_ID" above; or (b) you typoed the value
in your env file. Fix the typo or wipe the DB; restart.
---
## Appendix: alternatives to staging-on-mainnet
Three other options exist. None of them are recommended for
pre-launch testing today, but they're documented here so you
know the landscape.
### A community-maintained Blurt testnet does exist
Hosted by @nalexadre (the same operator who maintains
[BeBlurt](https://beblurt.com) and the
[blurt-nodes-checker](https://gitlab.com/beblurt/blurt-nodes-checker)
library Morphit cites in `OPERATIONS.md §22`). Documented
in this 2023 blog post:
[Blurt Blockchain Testnet with Nexus](https://beblurt.com/@nalexadre/blurt-blockchain-testnet-with-nexus-1690926923012).
**Testnet access details:**
| Field | Value |
|---|---|
| RPC endpoint | `https://testnet-rpc.beblurt.com` |
| Chain ID | `1df54a5cc86f7c7efee2402e1304df6eae24eb8766a63c0546c1b2511cf5eba6` |
| Address prefix | `TST` (mainnet uses `BLT`) |
| Asset symbol | `TESTS` (mainnet uses `BLURT`) |
| Companion CLI | [blurt-tools-cmd](https://gitlab.com/beblurt/blurt-tools-cmd) (GPLv3) |
The endpoint is live (`curl https://testnet-rpc.beblurt.com/`
returns `{"status":"OK"}`).
**Why Morphit can't use it today:** Morphit's codebase hard-
codes the mainnet address prefix (`BLT`) and asset symbol
(`BLURT`) in ~7 places — pubkey validators, fee-amount
regexes, frontend chain-verify, and the dblurt Client
constructor's address-prefix arg. Pointing the indexer at
`testnet-rpc.beblurt.com` today produces validation failures
on every chain op it tries to apply.
Making Morphit testnet-aware is a tracked item in
`docs/REVISIT-LIST.md` Section D — estimated ~2-3 hours of
careful work plus smoke regression — but **no operator
currently needs it before launch**, so it's deferred.
**If you just want to explore the testnet directly without
Morphit on top:** the `blurt-tools-cmd` CLI from the same
author talks to `testnet-rpc.beblurt.com` out of the box and
is the canonical way to send TESTS, create testnet accounts,
and exercise basic chain ops. Useful for sysadmins who want
to understand Blurt at the chain level without spending real
BLURT. Not connected to Morphit in any way; purely a Blurt
chain explorer/wallet.
### Self-hosted Blurt testnet via Docker
Run `blurtd` locally in test mode (see Blurt's witness
documentation for the Docker recipe) and point your tools at
`http://localhost:8091`. Same Morphit code-change blocker
as the public testnet; useful only if you're hacking on
Blurt protocol internals, not for app-layer testing.
### Mock chain via smoke tests only
Don't actually connect to a chain. Validate the deployment
using just the bundled smoke tests:
```sh
cd /opt/morphit
bash scripts/run-smokes.sh
```
The smoke suite covers 1179+ scenarios end-to-end without
touching the real network. This validates that your build,
schema migrations, and operator-config plumbing are all
working — but doesn't exercise live RPC behavior. Use it as
a pre-deploy sanity check, not as a substitute for actual
staging.
---
## See also
- [`docs/RUN-A-MORPHIT-NODE.md`](RUN-A-MORPHIT-NODE.md) — full
first-time deployment walkthrough.
- [`docs/OPERATIONS.md`](OPERATIONS.md) — operator runbook
(backups, top-ups, RPC list updates, etc.).
- [`apps/indexer/README.md`](../apps/indexer/README.md) —
indexer-specific operational notes.
- `ops/env/indexer.env.example` — full annotated env file
with every available knob documented.