Migrating from strfry

Move an existing strfry relay's events into nostrfy with one command — preparation, dry run, migration, verification and rollback.

At a glance

nostrfy migrate-strfry reads strfry's own export format (JSONL, one NIP-01 event per line), so it works across strfry database versions and does not depend on strfry's internal LMDB schema. It never writes to the strfry database.

MigratedNot migrated
Every stored event (replaceable/addressable semantics applied)strfry settings with no nostrfy equivalent (the merge report lists each with a reason)
NIP-40 expiry — already-expired events are skippedBlossom media and its owner mappings (strfry has no Blossom server)
NIP-09 deletions, including re-publication blocks for events strfry had already deletedAccess lists (NIP-86 bans, relay pubkey lists, Blossom allowlist)
NIP-29 9005/9008 moderation side effectsNIP-43 invite codes (issue new ones with createclaim)
First-seen timestamps (when the new-pubkey gate is configured)NIP-62 vanish requests unless --apply-vanish is given
NIP-29 groups, NIP-43 roles and their relay-signed metadata, rebuilt on the first startThe relay's own identity/keys (they live in nostrfy.toml)
The equivalent strfry settings, offered for merge into nostrfy.toml (optional)

Expected skips in the summary: ephemeral events (kinds 20000-29999, which nostrfy never stores) and already-expired events.

Quick start

sh
# 1. stop the nostrfy relay (the migration needs the database directory)
nostrfy --config /etc/nostrfy/nostrfy.toml stop

# 2. dry run — parses and verifies every event, writes nothing
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
    --strfry-db /var/lib/strfry-db --dry-run

# 3. import
nostrfy --config /etc/nostrfy/nostrfy.toml migrate-strfry \
    --strfry-db /var/lib/strfry-db

# 4. start — NIP-29 groups and NIP-43 roles rebuild from the imported events
nostrfy --config /etc/nostrfy/nostrfy.toml start
The migration is offline
It writes directly to database.path and refuses to run while a nostrfy daemon (or another migration) holds the database directory. Stop the relay first. strfry itself may keep running — strfry export reads a consistent snapshot.

Requirements

  • The strfry binary (for --strfry-db), or a JSONL file you exported yourself.
  • nostrfy v0.1.15 or newer (the migrate-strfry subcommand).
  • The nostrfy config for the target relay, with database.path, public_url and private_key set.
  • Free disk space: roughly the size of the strfry export plus its indexes. The NIP-50 word index adds some more; on a very tight disk you can disable it (database.search_index = false), migrate, and re-enable it later (the index is rebuilt at startup).
  • No running nostrfy instance on the target database.path.

Prepare the config

toml
[relay]
name = "My Relay"
public_url = "wss://relay.example.com"   # needed for NIP-42/62/98 and NIP-29 metadata
private_key = "..."                       # needed for NIP-29/43 relay-signed metadata

[server]
host = "0.0.0.0"
port = 8080

[database]
path = "/var/lib/nostrfy"
map_size = 1073741824

Generate the relay key with nostrfy genkey if you do not have one, then validate:

sh
nostrfy --config /etc/nostrfy/nostrfy.toml check

Merging the strfry settings (optional)

Before the database is opened, migrate-strfry looks for strfry's config (--strfry-config, then $STRFRY_CONFIG, /etc/strfry.conf, ./strfry.conf), prints the settings that have a nostrfy equivalent and differ from your nostrfy.toml, and asks whether to merge them. Only the listed keys are rewritten — comments and every other line are preserved, and a value that would make the config invalid is skipped with its reason while the rest still merge.

  • --merge-config applies without asking (for scripts); --no-merge-config skips the step.
  • With no terminal, proposals are printed and the merge is skipped unless --merge-config is given.
  • --dry-run prints the proposals but never writes.

Dry run

Always look before you leap — a dry run parses and verifies the whole export without touching the database:

sh
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db --dry-run

A non-zero bad signature count means the export contains events strfry accepted without verification; they will be skipped. If you trust them, pass --no-verify to import them anyway.

Migrate

Pick one of three input modes — all produce the same result:

sh
# Option A — nostrfy runs `strfry export` itself (strfry on PATH)
nostrfy migrate-strfry --strfry-db /var/lib/strfry-db

# Option B — you exported to a file
strfry export > /tmp/strfry-export.jsonl
nostrfy migrate-strfry --input /tmp/strfry-export.jsonl

# Option C — pipe (stdin is the default input)
strfry export | nostrfy migrate-strfry
FlagWhy
--strfry-bin <PATH>strfry is not on PATH
--since <UNIX>Resume/catch-up: events with this created_at or newer (inclusive)
--apply-vanishHonor NIP-62 vanish requests found in the export (off by default)
--no-verifySkip signature verification for trusted dumps (faster)
--batch <N>Events per database transaction (default 512)
--dry-runParse and verify only

The migration is safe to re-run: duplicates are skipped and the deletion side effects are re-applied, so an interrupted run can simply be repeated (or resumed with --since).

Start and verify

The first start rebuilds the NIP-29 group store and the NIP-43 role store from the imported events and republishes the relay-signed metadata (39000/39001/39002/39005 per group, the 13534 membership list). On a large database this can take a moment; watch the log.

sh
R=wss://relay.example.com      # for nak (WebSocket)
H=https://relay.example.com    # for curl (HTTP)

nak relay "$R"                              # relay answers and advertises its NIPs
curl -s "$H/api/v1/query?limit=1"           # events are served
nak req -i <deleted-event-id> "$R"          # a deleted event stays gone
nak req -k 39000 "$R"                       # NIP-29 group metadata (if migrated)
nak req --auth --force-pre-auth --sec <nsec> -k 13534 "$R"   # NIP-43 membership (AUTH)

For an exact count comparison, strfry scan '{}' | wc -l minus the ephemeral/expired events reported by the migration summary should equal what clients can retrieve.

Resuming an interrupted migration

Do not start the relay before re-running
The NIP-29 group side effects (9005/9008) are applied after the import; an interrupted run has stored those events but not their deletions yet, so the first start could serve group history the deletion was meant to remove. Re-run the migration first — it completes the side effects (the purge is idempotent) — then start the relay.
  • Exported to a file / piped: re-run the same command. Duplicates are skipped and the deletion blocks are re-applied.
  • Used --strfry-db: the summary prints a resume hint; re-run with that --since (inclusive, so the boundary second is re-imported and deduplicated).
  • If the run failed with database writer unavailable, check free disk space and database.map_size, then re-run.

Rollback

The migration only writes to the nostrfy database. To roll back, stop the relay and restore the pre-migration database or remove it:

sh
nostrfy --config /etc/nostrfy/nostrfy.toml stop
rm -rf /var/lib/nostrfy            # or restore the pre-migration backup

Troubleshooting

MessageCause / fix
cannot lock the database directory ...; stop the relay before migratingA nostrfy daemon (or another migration) holds the directory: nostrfy stop first
strfry database directory ... does not exist--strfry-db must name the directory that contains data.mdb
cannot run 'strfry': ...Install strfry, set --strfry-bin, or use --input
database writer unavailable; the migration did not completeThe writer thread stopped or the queue is overloaded: check disk/map size, re-run (safe)
group purge for <id> did not completeThe purge was interrupted: re-run the migration
High bad signature countThe strfry DB contains unverified events: inspect them; import with --no-verify only if you trust the source
NIP-29 metadata missing after the startNo relay.private_key: run nostrfy genkey and restart
The settings merge is not offeredstrfry's config was not found: pass --strfry-config /etc/strfry.conf

Checklist

  • nostrfy relay stopped
  • strfry database and nostrfy config backed up
  • nostrfy check passes
  • strfry settings merged (or the report reviewed)
  • Dry run reviewed (no unexpected bad signatures)
  • Migration completed without errors
  • Relay starts; group/role rebuild logged
  • Event counts match (minus ephemeral/expired)
  • Deleted events stay gone (re-publish rejected)
  • Private-group visibility checked anonymously and as a member
  • Reverse proxy / DNS / client relay lists updated
Still running strfry?
If strfry stayed live during the export, do a catch-up run once you are ready to switch: stop nostrfy, re-run the migration with --since <last created_at>, then start it again.