rendered from docs/zone-handoff.md e9f6b9d42c0a
Handing a zone to a new host
An account here is a did:web name in a zone this deployment serves
(the names an agent has). Its DID document is served by whoever
answers HTTPS for that name. Moving the deployment keeps every DID, so apps
keep their at:// URIs, as long as two things move together: the zone’s
delegation, and the data volume holding the signing keys.
This page moves a whole deployment. Moving one account to another PDS is evacuation.
What moves, and where it lives
| Piece | Lives | Moves by |
|---|---|---|
| Accounts, records, ledger, signing keys | The log and pds.checkpoint on the data volume |
The volume |
| Blobs | blobs/ on the data volume |
The volume |
| OAuth grants, spent proofs, e-stop, policy set, firehose sequence | pds.grants, pds.proofs, pds.estop, pds.policy, pds.sequence on the data volume |
The volume |
| The server’s own account and key | The log | The volume |
| ACME account key, certificates | tls/ on the data volume |
The volume |
| The hosted zone | Route53, terraform-aws-didbot-pds/route53.tf |
A new zone, delegated again from the parent |
| The delegation | The parent zone. For pds.did.bot, infra/site/pds.auto.tfvars |
An apply of the parent |
| Rendered config | infra/pds variables |
The same variables in the new stack |
| The operator’s claim and policy records | The operator’s own repository | Nothing. They name the server’s DID, which is the zone and does not change |
Rules
- Stop the source before the snapshot, and never start it again. Two servers holding one set of keys sign two histories for every account. A relay keeps whichever it saw last, and nothing reconciles them.
- Snapshot the volume; do not copy the directory. The volume is the consistency unit (Backups and restore).
- The zone and the subzone layout do not change. A DID is a hostname (Rules across upgrades).
A new instance in the same AWS account
The data volume outlives the instance (terraform-aws-didbot-pds/ec2.tf). Any change that
replaces the instance, such as a new ami_id or a change to its user data,
stops the old instance, detaches the volume and attaches it to the new one. The boot
script mounts the volume by its id and does not format a volume that holds a
filesystem. The Elastic IP moves with the association, so DNS does not
change.
A lost volume, restored from backup
Follow Restoring, Mark step included: the backup
is up to a day old, and relays hold what it lost. Restoring through
Terraform, so infra/pds manages the restored volume, is an open item in
evacuation.
A new host in another AWS account
The source and destination are two infra/pds stacks with the same
root_zone and nested_zones, in two accounts.
- Announce the window. Every name in the zone is offline from step 2 until step 9.
- Stop the source.
systemctl disable --now didbot-pds, then confirmdocker ps --filter name=didbot-pdslists nothing, andsync. Stop the instance too, so nothing restarts the unit. - Snapshot.
aws ec2 create-snapshot --volume-id <data volume>and wait forcompleted. The source stopped cleanly, so this snapshot holds every write. - Share. The snapshot is encrypted under the source stack’s
aws_kms_key.ebs, whose policy grants the source account and its backup role. The destination account needs decrypt on that key to copy the snapshot. That grant is the first open item in evacuation. With it,aws ec2 modify-snapshot-attribute --create-volume-permissionshares the snapshot with the destination account. - Destination key and zone. In the destination, run
Bring-up steps 1 to 3 with
-target=module.pds.aws_kms_key.ebsadded to step 2. Setoperator_didto the operator who will answer for the deployment. - Copy and import the volume.
aws ec2 copy-snapshotre-encrypts the shared snapshot under the destination’saws_kms_key.ebs. Create a volume from the copy in the instance subnet’s availability zone, with the size and typedata_volume_size_gbnames, thentofu -chdir=infra/pds import module.pds.aws_ebs_volume.data <volume id>. A volume whose key, size, type or zone differ from the stack is a replacement, andprevent_destroystops that plan. - Delegate. Point the parent at the destination zone’s four name
servers: Bring-up steps 4 and 5. Leave the source zone in place until the
parent’s
NSTTL has passed. - Start.
tofu -chdir=infra/pds apply. The instance mounts the imported volume and chowns/data/pds. The server serves the certificate intls/while it is valid and the run’sacme_environmentmatches the one it was issued under. It writes theCAArecord for the ACME account intls/into the new zone. - Verify. Run Verifying a restore.
Leave out the Mark step: nothing was lost, and
pds.sequencecarries the firehose position. - Claim, if the operator changed. The server polls the configured
operator’s repository for its claim and its policies. Until
didbot operate <root_zone> <operator_did>runs, it serves reads and refuses writes (the server lifecycle). The new operator publishes their policy records first. - Announce.
didbot login, thendidbot announce(Announcing to relays). A relay that stopped dialing during the window subscribes again from its cursor. - Retire the source. Keep the source volume, key and zone until the
destination has its own recovery points. Deleting the source
aws_kms_key.ebsmakes the source’s recovery points unreadable.
Sessions across a handoff
| Session | After the handoff |
|---|---|
| OAuth grants | Kept. pds.grants moves with the volume, and the issuer is the zone’s own endpoint, which does not change. Clients refresh as before |
Account tokens from bot.did.createSession |
Kept, until their own expiry. They are in the log |
| Sign-ins in flight | Lost. They are in memory. The app starts the sign-in again |
| The operator’s dashboard session | Lost. didbot login again |
| Agent hosts | Unchanged. didbot-agentd reaches the server by its zone name |
After a restore from backup instead of a stopped source, grants issued after the recovery point are gone. Each app holding one is refused at its next refresh and authorizes again.