Opening a Strait

Series: raising-an-island · Part 2

A lane connects two islands. Signals cross it in both directions — speech, arrivals, departures, the passage of items — attenuated by distance and filtered by each island’s customs policy. Players see a lane as a strait: a crossing with weather, not a pipe with throughput.

Opening a lane requires both operators. One-sided configuration produces a refused handshake, narrated as fog on the strait. Both sides must declare the other before the beacon lights.

This guide walks through opening your first lane with another operator.


Before you begin

You and the other operator need to exchange three things out of band — before touching any configuration:

  1. Your hostname — the address travelers (and porthmos) connect to
  2. Your realm’s public key — the Ed25519 key your realm uses to sign every handshake
  3. Agreed terms — trust level you’ll assign each other’s travelers, attenuation distance

Do this over a channel you trust. A substituted public key means a lane that looks open but whose traffic you cannot verify. Peering with a realm whose key you haven’t confirmed is peering with whoever controls that key.

Get your identity to share:

watch> identity

Realm:    winding-stair
Hostname: winding-stair.example.com
Pubkey:   ed25519:3b4af2c9…
Seal:     ┌╌╌┐╎◆╎└╌╌┘

Or from outside the realm:

pharos identity --realm winding-stair/

The other operator runs the same and shares their output with you. You can also verify a realm’s pubkey by connecting as guest and running /about — the realm announces its own identity at the gate.


Configuring the peer

Add a peer block to your realm.toml:

[[realm.federation.peer]]
hostname    = "thornwood.example.com"
pubkey      = "ed25519:9c7de1a4…"
trust       = "partner"
attenuation = 1

A few decisions here:

trust — the default trust level assigned to travelers arriving from this realm. It does not affect how your locals are treated when they visit thornwood; that is thornwood’s decision. Set it to match the relationship: neutral for new acquaintances, known or partner for operators you know well. Travelers can always be individually adjusted later.

attenuation — the signal distance in narrative hops. 1 means a direct neighbor: voices from thornwood arrive clearly, arrivals are announced in border rooms, the crossing feels immediate. Use 1 for all direct peers. Higher values are for realms that are geographically distant in your island’s fiction — a far port you are technically linked to but that should feel far away.

The other operator adds a corresponding block pointing at your realm, using your hostname and pubkey, with whatever trust and attenuation they have chosen.


Firewall

Port 3456 (or whatever [realm.federation] port is set to) must accept inbound TCP from the peer’s address. Open it narrowly:

# Allow porthmos from thornwood only
ufw allow from <thornwood-ip> to any port 3456 proto tcp

There is no reason to expose the federation port to the world. The peer’s IP is fixed; the rule should be specific.

If you are behind NAT or a load balancer, ensure the peer’s TCP connection reaches pharos directly. Porthmos uses mutual TLS — the connection cannot be terminated and re-established by a proxy.


Reloading and the handshake

Once both sides have updated their configuration, reload:

systemctl reload pharos-winding-stair

Pharos reads the new peer block and attempts the porthmos handshake immediately. The handshake exchanges realm metadata, trust policies, and signal attenuation parameters. If the other side is not yet ready — their reload is still pending — pharos retries on a backoff schedule. The lane opens automatically when both sides are available.

You do not need to coordinate the reload timing. Configure, reload, wait.


Verifying the lane

Check federation status from admin mode:

watch> lanes

thornwood.example.com  ◈ active   partner  dist:1  latency:18ms

A lane in the ◈ active state is carrying traffic. If you see ○ pending, the other side has not yet opened their end — the handshake is retrying. If you see ○ refused, the other side’s pharos rejected the connection — check that the pubkeys match and that the firewall is open on both sides.

Inspect a specific lane:

watch> lane thornwood

Peer:        thornwood.example.com
Pubkey:      ed25519:9c7de1a4…
Status:      ◈ active
Trust:       partner
Attenuation: 1
Latency:     18ms
Established: third watch, year of federation I
Last signal: 4m ago

What players see

When the lane opens, travelers in border rooms on both islands see:

◈ A beacon to the east holds steady — thornwood is on the line.

The room header’s federation counter updates:

┌─ ⌂ The Common Room ──────────────── winding-stair · 22:14 · ◈ 1/1 ─┐

1/1 — one peer reachable, one peer configured. When the lane carries its first cross-realm signal, players near the border hear it marked:

◈ [thornwood] Old Mira says, "The road from the east is clear tonight."

The glyph and the realm name distinguish cross-realm speech from local. Players are never silently exposed to foreign narration — the origin is always marked.


Adjusting after the lane is open

Change trust level for a specific traveler from thornwood:

watch> trust did:archi:9f2a… known

Change the trust assigned to all thornwood arrivals by editing the peer block in realm.toml and reloading. The lane stays open during a reload — traffic pauses for the duration of the handshake re-exchange, which takes under a second.

Change attenuation the same way. Increasing attenuation makes cross-realm signals feel more distant; decreasing it makes thornwood feel like a closer neighbor. Players in border rooms will notice the shift in how foreign speech sounds.


Suspending a lane

To take a lane dark temporarily without removing the configuration:

watch> suspend lane thornwood

Lane to thornwood suspended. Beacon dims.

Players see:

◈ The beacon to thornwood flickers — once, twice — and goes dark.
The wind carries nothing from that direction now.

The lane is not closed — the configuration is intact, and the other operator’s pharos is still trying to reach yours. The suspension is local. Resume with:

watch> resume lane thornwood

Lane to thornwood resuming. Beacon steadying.

Suspension is for maintenance, planned downtime, or cooling-off periods. It does not notify the other operator. They will see your side go dark and read it as an outage unless you tell them otherwise.


Closing a lane

To close a lane permanently, remove the peer block from realm.toml and reload:

# Remove or comment out the block:
# [[realm.federation.peer]]
# hostname    = "thornwood.example.com"
# ...
systemctl reload pharos-winding-stair

Your side stops accepting or initiating connections to thornwood. Pharos does not notify thornwood — the lane simply goes silent from their end. Their watch> lanes will show your realm as ○ dark.

The other operator’s peer block continues to point at you. Their pharos will retry the handshake indefinitely until they also remove your entry. There is no harm in this — refused connections are not errors in porthmos, they are weather.

Inform the other operator. Closing a lane without notice is a sovereign act; it is permitted. It is also the kind of thing that shapes the reputation of a keeper.


Confederations

A confederation is a group of islands that peer with each other by mutual agreement — no central registry, no required topology. Each island adds peer blocks for every other member it chooses to federate with. The confederation exists only as a pattern of bilateral trust relationships.

If three islands want to form a confederation, each adds two peer blocks (one for each of the others). There is no join confederation command. The confederation is the sum of its lanes.

Some operators publish their realm’s pubkey and federation policy publicly, to invite peering requests from strangers. Others peer only with operators they know personally. Both are valid. The protocol does not distinguish.