# Office1 service re-IP -- move NetBox + Tailscale into the D-120 static band

**Gated mutation on TWO RUNNING services.** Do not run until **D-120 is ADOPTED** (the operator has
ratified the band layout AND the target addresses). Present each step, justify it as minimal, get
individual approval. This is also the reference procedure the DCs reuse for their own compose /24s.

## Target (D-120 PROPOSED; confirm against the adopted decision before running)

    office1-netbox     10.10.1.201 -> 10.10.1.10   (static, band .2-.49)
    office1-tailscale  10.10.1.202 -> 10.10.1.11   (static, band .2-.49)

Both VMs are MAAS-COMPOSED LXD VMs on `voffice1`; their addresses come from MAAS. The edge's static
route `10.10.1.0/24 -> 10.10.0.20` is /24-wide and is UNAFFECTED by the change.

## Preconditions (READ-ONLY)

1. D-120 ADOPTED; target addresses confirmed.
2. Coordinate with sandbox/C2 work -- the NetBox re-IP briefly interrupts the sandbox NetBox.
3. Record current state: `office1-netbox 10.10.1.201`, `office1-tailscale 10.10.1.202` (as-built).
4. Access: `ssh -J jessea123@10.10.0.20 ubuntu@10.10.1.201` (and `.202`); MAAS on `voffice1`
   (`10.10.0.20`), secrets in `/root/maas-secrets/` on the box (operator-held).

## Step 1 -- MAAS: static-assign the new address to each VM's interface [MUTATION: gated]

For each machine, set its interface on the `10.10.1.0/24` subnet to **static-assign** the target
address (a fixed IP outside MAAS's `.100-.200` dynamic range). The `.2-.49` band is already outside
the dynamic range, so NO dynamic-range edit is required.

    <PENDING VERIFICATION: the exact MAAS CLI. In the MAAS UI: Machine -> Network -> the interface on
    10.10.1.0/24 -> Edit -> Mode = "Static assign" -> IP = 10.10.1.10 (netbox) / .11 (tailscale).
    CLI form is roughly `maas $PROFILE interface link-subnet <system_id> <iface_id> mode=STATIC
    subnet=<subnet_id> ip_address=10.10.1.10` -- CONFIRM the subcommand/args against the installed
    MAAS 3.7 before running; do not run a guessed command against live MAAS.>

## Step 2 -- Apply the new address on the VM [MUTATION: gated]

A composed VM picks up the MAAS assignment on a network reconfigure or redeploy.

    <PENDING VERIFICATION: whether MAAS pushes the static IP without a redeploy, or whether the VM
    needs `netplan apply` / a reboot / a MAAS re-deploy. Prefer the least disruptive path that
    actually takes -- verify the address changed by reading `ip -4 addr` on the VM, NOT by assuming.>

## Step 3 -- Fix each SERVICE's own address config [MUTATION: gated]

- **NetBox (`office1-netbox`, netbox-docker):** NetBox binds `0.0.0.0:8000`, so it follows the host
  IP -- BUT `ALLOWED_HOSTS` / `CSRF_TRUSTED_ORIGINS` may name the old `10.10.1.201`.
  `<PENDING VERIFICATION: read the netbox-docker env (e.g. /opt/netbox-docker/env/netbox.env or the
  compose .env on the box) -- if ALLOWED_HOSTS lists 10.10.1.201, add/replace 10.10.1.10 and restart
  the stack (`docker compose up -d`). If it is `*`, no change needed.>`
- **Tailscale (`office1-tailscale`):** it advertises `10.10.0.0/22` as a subnet router; the ADVERTISED
  routes do not change, but its own LAN interface address does. `<PENDING VERIFICATION: confirm
  tailscaled still routes after the interface IP change -- `tailscale status`, and that 10.10.0.0/22
  is still advertised/approved. A `tailscale up ... --advertise-routes=10.10.0.0/22` re-assert may be
  needed; the node identity/key is unchanged.>`

## Step 4 -- Verify on the wire (READ-ONLY)

    ssh -J jessea123@10.10.0.20 ubuntu@10.10.1.10 'ip -4 addr'      # netbox at .10
    curl -s -o /dev/null -w "%{http_code}\n" http://10.10.1.10:8000/api/    # 403/200 = up
    ssh -J jessea123@10.10.0.20 ubuntu@10.10.1.11 'tailscale status'        # tailscale at .11

## Step 5 -- Update the 20 repo references + the importer allowlist [repo change; gated]

`10.10.1.201 -> 10.10.1.10` and `10.10.1.202 -> 10.10.1.11` across (measured 2026-07-15):

- **CODE (must, or the guard breaks):** `SANDBOX_HOSTS` in `netbox/roles-aggregates-import.py` and
  `netbox/dc-dc-prefixes-import.py` (the `.201` entry -> `.10`); `netbox/d115-office-carve.py`'s
  `SANDBOX_HOSTS`; `tests/dc-dc-prefixes-import/test_logic.py` (the sandbox-URL host it sets);
  `tests/roles-aggregates-import/run-tests.sh` (the SANDBOX_HOSTS assertion).
- **Docs (forward-looking):** `docs/vr1-office1-as-built.md`, `docs/session-ledger.md`,
  `docs/dc-dc-netbox-buildout-scope.md`, `docs/dc-dc-deployment-workflow.md`,
  `runbooks/dc-dc-phase1-office1-standup.md`. Leave dated changelogs as historical snapshots.
- Re-run `bash scripts/run-tests-all.sh` (the SANDBOX_HOSTS harnesses must stay green) + `repo-lint`.

## Step 6 -- Record the D-120 carve in NetBox (via the sandbox loop)

Register `10.10.1.0/24`'s child ranges (static `.2-.49` / dynamic `.100-.200` / node `.201-.254`) and
the two service IP assignments in the sandbox NetBox, then feed upstream with C2. NetBox is the IPAM
authority for this; do not leave the layout recorded only in prose.

## Rollback

Re-run Steps 1-3 with the old addresses (`.201`/`.202`), `git revert` the Step-5 repo change. Nothing
here is destructive -- the VMs and their data are untouched; only the L3 address changes.

## DC replication note

When a DC's compose /24 is built, its services get D-120 static-band addresses FROM DAY ONE via this
same procedure -- there is no "assign in the node band then re-IP later" for the DCs. This runbook is
the reference.
