From 66a1aaad69ef59c85823be85156f229d18ee67d7 Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:24:10 +0200 Subject: [PATCH 1/6] docs: on-site makerspace field guide for CRS310 prep Standalone printable checklist: bring-list, access via WinBox MAC, confirm RouterOS, upgrade+pin firmware, record facts, reset to no-defaults, temp IP + SSH, addressing decisions, physical finish. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/makerspace-switch-fieldguide.md | 165 +++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 docs/makerspace-switch-fieldguide.md diff --git a/docs/makerspace-switch-fieldguide.md b/docs/makerspace-switch-fieldguide.md new file mode 100644 index 0000000..54999d2 --- /dev/null +++ b/docs/makerspace-switch-fieldguide.md @@ -0,0 +1,165 @@ +# Makerspace Field Guide — Preparing the CRS310 Switch + +**Print this and bring it.** This is the exact, on-site procedure to get the MikroTik +**CRS310-8G+2S+IN** ready so Ansible can take over. Total time: ~30–45 min (most of it +the firmware upgrade). Work on a **bench/isolated network** — do **not** plug the switch +into the live makerspace network until VLANs are configured later (avoids loops and +DHCP/IP conflicts). + +When you're done, you'll have: the switch on a known firmware, wiped to a clean slate, +reachable over SSH at a temporary IP, and a few facts written down for me to drop into +`host_vars`. + +--- + +## Bring with you + +- [ ] The CRS310 + its PSU. +- [ ] A laptop with **WinBox** (download from mikrotik.com/download) — or just a browser for WebFig. +- [ ] One Ethernet cable (laptop ↔ a 2.5G port). +- [ ] Internet for the switch during the upgrade (a cable from an existing LAN/uplink, **temporarily**, with DHCP — unplug it again before the final steps). +- [ ] The **SFP+ module or DAC** for the 10G uplink (to fit physically; we don't cable the real uplink yet). +- [ ] This guide + something to write the recorded facts on (or a phone note). + +--- + +## Step 1 — Power on and get in + +1. Power the switch. Wait ~1 min for it to boot RouterOS. +2. Connect your laptop to **ether1** (a 2.5G port). +3. Open **WinBox → Neighbors tab**. The switch appears (by IP `192.168.88.1` and/or by MAC). + - **Tip:** click the **MAC address** (not the IP) to connect — this works even when the + switch has no IP, which matters in Step 5. +4. Log in: user `admin`, password **blank** (just press Enter). RouterOS 7 may ask you to + set a password — you can set a temporary one or skip; Ansible will set the real one later. + +> No WinBox? Browse to `http://192.168.88.1` (WebFig) instead. The CLI commands below are +> typed in **WinBox/WebFig → New Terminal**. + +--- + +## Step 2 — Confirm it's running RouterOS (not SwOS) + +The CRS310 can dual-boot SwOS, but we need **RouterOS** for VLAN filtering + Ansible. + +- In terminal: `/system/routerboard/print` +- It should report RouterOS. If the device booted **SwOS** (different, simpler web UI), + switch the boot OS: in SwOS go to the **System** page and set boot to RouterOS, or use + the reset/boot-OS toggle, then reboot. (You want the full RouterOS interface.) + +--- + +## Step 3 — Upgrade and pin the firmware + +This needs internet for the switch. Plug a DHCP uplink into **ether8** temporarily. + +1. Give the switch internet briefly: it should pull a DHCP lease on the uplink port, or in + terminal: `/ip/dhcp-client/add interface=ether8 disabled=no` +2. Update RouterOS: + ``` + /system/package/update/set channel=stable + /system/package/update/check-for-updates + /system/package/update/download + /system/reboot + ``` + (Or WinBox: **System → Packages → Check For Updates → Download & Install**.) +3. After reboot, upgrade the bootloader (RouterBOOT) to match: + ``` + /system/routerboard/upgrade + /system/reboot + ``` +4. **Write down the final version:** `/system/resource/print` → the `version` line. + ➜ **Record as `RouterOS version: ______`** (this becomes `switch_firmware_target`). +5. **Unplug the temporary internet uplink** and remove the DHCP client: + `/ip/dhcp-client/remove [find]` + +--- + +## Step 4 — Record the device facts + +Run `/system/routerboard/print` and `/system/resource/print` and write down: + +- [ ] **Model:** ____________________ (should be CRS310-8G+2S+IN) +- [ ] **Serial:** ____________________ (also on the sticker underneath) +- [ ] **Base MAC:** ____________________ +- [ ] **RouterOS version:** ____________________ (from Step 3.4) + +--- + +## Step 5 — Wipe to a clean slate (no default config) + +This makes Ansible the single owner of the whole configuration. + +1. In terminal: + ``` + /system/reset-configuration no-defaults=yes skip-backup=yes + ``` + (Or WinBox: **System → Reset Configuration** → tick **No Default Configuration** and + **Do Not Backup** → **Reset**.) +2. The switch reboots. It now has **no IP and no services** — WinBox-by-IP won't find it. +3. Reconnect using **WinBox → Neighbors → click the MAC address** (this is why we use MAC). + Log in as `admin` with a **blank** password. + +--- + +## Step 6 — Give it a temporary IP + enable SSH (so Ansible can reach it) + +In the terminal (laptop still on **ether1**): + +``` +/ip/address/add address=192.168.88.1/24 interface=ether1 +/ip/service/enable ssh +/ip/service/print +``` + +Then on your laptop, set a static IP `192.168.88.2` / `255.255.255.0` and confirm SSH: + +``` +ssh admin@192.168.88.1 +``` + +If that logs in, **you're done** — leave the switch powered and on the bench. + +> ⚠️ Keep a **WinBox MAC session** open as your lifeline whenever you change network +> settings. If you ever lock yourself out, MAC-telnet/WinBox-by-MAC still works; a full +> **Netinstall** (mikrotik.com/download) is the last-resort recovery. + +--- + +## Step 7 — Decide the real addressing (write it down for me) + +I need these to fill in `host_vars/crs310-maker.yml`. Decide with whatever the makerspace +network plan is (or we can finalize together): + +- [ ] **Management IP + mask** (real, not the temp one): ____________________ +- [ ] **Management VLAN ID:** ____________________ +- [ ] **Default gateway:** ____________________ +- [ ] **Upstream uplink port** (which SFP+ / port goes to the OPNsense/router): ____________________ +- [ ] **DNS / NTP server IP** (usually the gateway): ____________________ + +(If the makerspace VLAN plan isn't settled yet, that's fine — we ship a placeholder and +fill these in later. The switch just needs to be reachable per Step 6.) + +--- + +## Step 8 — Physical finish + +- [ ] Fit the **SFP+ module/DAC** into `sfp-sfpplus1` (don't cable the live uplink yet). +- [ ] Mount/label the switch. + +--- + +## When you're back + +Bring me: +1. The recorded facts (Step 4) and addressing decisions (Step 7). +2. Confirmation that `ssh admin@192.168.88.1` (or your temp IP) works. + +Then I'll: create the empty `MakerFLOSS_Mikrotik` repo on `forgejo.makerfloss.eu`, drop +your facts into `host_vars`, and run **`play_bootstrap.yml`** — which creates your named +admin user, imports your SSH key, and hands the switch over to Ansible. After that, +`play_switch.yml` configures identity, services, VLANs, and backups. + +> **Do not connect the switch to the live makerspace network** until VLANs are configured +> (Task 7 in the implementation plan) — an unconfigured switch on the live net can cause +> loops or hand out the wrong VLAN. From be9ac7f78bdbb9c7219fc2d4ac6f73aa40d2a095 Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:26:09 +0200 Subject: [PATCH 2/6] chore: repo scaffolding (direnv, ansible.cfg, lint, requirements) --- .ansible-lint | 8 ++++++++ .envrc | 9 +++++++++ .gitignore | 5 +++++ .yamllint | 11 +++++++++++ ansible.cfg | 19 +++++++++++++++++++ requirements.txt | 10 ++++++++++ requirements.yml | 6 ++++++ 7 files changed, 68 insertions(+) create mode 100644 .ansible-lint create mode 100644 .envrc create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 ansible.cfg create mode 100644 requirements.txt create mode 100644 requirements.yml diff --git a/.ansible-lint b/.ansible-lint new file mode 100644 index 0000000..b567eb6 --- /dev/null +++ b/.ansible-lint @@ -0,0 +1,8 @@ +--- +profile: production +skip_list: + - line-length + - no-changed-when +exclude_paths: + - .venv/ + - backups/ diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..45306f4 --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +# Create .venv automatically if it doesn't exist +if [ ! -d .venv ]; then + python3 -m venv .venv + .venv/bin/python -m pip install -U pip setuptools wheel +fi + +# Activate the environment manually (avoids Python 3.13 deprecation warning) +export VIRTUAL_ENV=$PWD/.venv +PATH_add .venv/bin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e1b4427 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.venv/ +*.retry +__pycache__/ +*.pyc +.DS_Store diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..e73b9ab --- /dev/null +++ b/.yamllint @@ -0,0 +1,11 @@ +--- +extends: default +rules: + line-length: disable + comments: + min-spaces-from-content: 1 + truthy: + allowed-values: ["true", "false", "yes", "no"] +ignore: | + .venv/ + backups/ diff --git a/ansible.cfg b/ansible.cfg new file mode 100644 index 0000000..c189fc8 --- /dev/null +++ b/ansible.cfg @@ -0,0 +1,19 @@ +[defaults] +inventory = inventories/prod/hosts.yml +roles_path = roles:~/.ansible/roles +collections_path = ~/.ansible/collections +host_key_checking = False +retry_files_enabled = False +interpreter_python = auto_silent +nocows = 1 +timeout = 30 +stdout_callback = yaml +bin_ansible_callbacks = True +vault_identity_list = makerfloss@~/.ansible/vault-keys/makerfloss.txt + +[persistent_connection] +command_timeout = 60 +connect_timeout = 60 + +[ssh_connection] +pipelining = True diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..3d5bd4d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +# Core Ansible +ansible==10.3.0 + +# Linting & validation +ansible-lint==24.7.0 +yamllint==1.35.1 + +# Network connection plugins / SCP for SSH key transfer to RouterOS +paramiko>=3.4.0 +scp>=0.15.0 diff --git a/requirements.yml b/requirements.yml new file mode 100644 index 0000000..9a04f2e --- /dev/null +++ b/requirements.yml @@ -0,0 +1,6 @@ +--- +collections: + - name: community.routeros + version: ">=3.0.0,<4.0.0" + - name: ansible.netcommon + version: ">=6.0.0,<8.0.0" From 3abb937a03b578341788dbe12b4c007ba8046631 Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:29:57 +0200 Subject: [PATCH 3/6] feat: inventory, connection group_vars, makerfloss vault identity Co-Authored-By: Claude Sonnet 4.6 --- group_vars/all.yml | 4 ++++ group_vars/mikrotik.yml | 12 ++++++++++++ inventories/prod/hosts.yml | 7 +++++++ 3 files changed, 23 insertions(+) create mode 100644 group_vars/all.yml create mode 100644 group_vars/mikrotik.yml create mode 100644 inventories/prod/hosts.yml diff --git a/group_vars/all.yml b/group_vars/all.yml new file mode 100644 index 0000000..acb81e8 --- /dev/null +++ b/group_vars/all.yml @@ -0,0 +1,4 @@ +--- +# Shared non-secret defaults across all hosts go here. +# Secrets live in the vault (see host_vars / a vaulted file), not in this file. +org_name: "MakerFLOSS" diff --git a/group_vars/mikrotik.yml b/group_vars/mikrotik.yml new file mode 100644 index 0000000..278ec97 --- /dev/null +++ b/group_vars/mikrotik.yml @@ -0,0 +1,12 @@ +--- +ansible_connection: ansible.netcommon.network_cli +ansible_network_os: community.routeros.routeros +ansible_user: admin +ansible_ssh_private_key_file: "~/.ssh/id_ed25519" + +# Domain enable-flags (day-2 play). Override per-host if needed. +switch_identity_enabled: true +switch_users_enabled: true +switch_vlans_enabled: true +switch_backup_enabled: true +switch_firmware_enabled: false # opt-in; upgrades are disruptive diff --git a/inventories/prod/hosts.yml b/inventories/prod/hosts.yml new file mode 100644 index 0000000..f406752 --- /dev/null +++ b/inventories/prod/hosts.yml @@ -0,0 +1,7 @@ +--- +all: + children: + mikrotik: + hosts: + crs310-maker: + ansible_host: 192.168.88.1 # temp mgmt IP until Task 4 sets the real one From 5c04b3405b605ed0ce03ca6e4625908b8b2b0ab3 Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:31:34 +0200 Subject: [PATCH 4/6] docs(group_vars): clarify ansible_user=admin is a bootstrap default Co-Authored-By: Claude Opus 4.8 (1M context) --- group_vars/mikrotik.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/group_vars/mikrotik.yml b/group_vars/mikrotik.yml index 278ec97..34aa542 100644 --- a/group_vars/mikrotik.yml +++ b/group_vars/mikrotik.yml @@ -1,6 +1,8 @@ --- ansible_connection: ansible.netcommon.network_cli ansible_network_os: community.routeros.routeros +# Bootstrap default. play_bootstrap.yml creates a named admin user and imports the +# operator SSH key; thereafter override ansible_user to that named user (host_vars). ansible_user: admin ansible_ssh_private_key_file: "~/.ssh/id_ed25519" From ad2c00f84aa21b038130b51fe5ecf1268f89787e Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:34:13 +0200 Subject: [PATCH 5/6] feat: role skeleton, host_vars, day-2 play (stubbed domains) Co-Authored-By: Claude Sonnet 4.6 --- host_vars/crs310-maker.yml | 23 +++++++++++ play_switch.yml | 6 +++ .../defaults/main.yml | 38 +++++++++++++++++++ .../makerfloss.mikrotik_switch/meta/main.yml | 10 +++++ .../tasks/backup.yml | 4 ++ .../tasks/firmware.yml | 4 ++ .../tasks/identity.yml | 4 ++ .../makerfloss.mikrotik_switch/tasks/main.yml | 25 ++++++++++++ .../tasks/users.yml | 4 ++ .../tasks/vlans.yml | 4 ++ 10 files changed, 122 insertions(+) create mode 100644 host_vars/crs310-maker.yml create mode 100644 play_switch.yml create mode 100644 roles/makerfloss.mikrotik_switch/defaults/main.yml create mode 100644 roles/makerfloss.mikrotik_switch/meta/main.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/backup.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/firmware.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/identity.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/main.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/users.yml create mode 100644 roles/makerfloss.mikrotik_switch/tasks/vlans.yml diff --git a/host_vars/crs310-maker.yml b/host_vars/crs310-maker.yml new file mode 100644 index 0000000..ec77567 --- /dev/null +++ b/host_vars/crs310-maker.yml @@ -0,0 +1,23 @@ +--- +# Identity facts recorded during Phase 0.6 (edit to match the device) +switch_identity_name: "crs310-maker" +switch_mgmt_vlan_id: 99 +switch_mgmt_address: "10.0.99.2/24" # EDIT: real mgmt IP +switch_mgmt_gateway: "10.0.99.1" # EDIT: real gateway +switch_dns_servers: "10.0.99.1" +switch_ntp_servers: "10.0.99.1" + +switch_admin_user: "sjat" + +# Real VLAN/port topology (EDIT to the makerspace plan when known) +switch_vlans: + - {id: 99, name: "mgmt"} + - {id: 10, name: "members"} +switch_bridge_ports: + - {interface: "ether1", pvid: 10, mode: access} + - {interface: "ether2", pvid: 10, mode: access} + - {interface: "sfp-sfpplus1", pvid: 1, mode: trunk, tagged_vlans: [99, 10]} + +# Firmware (opt-in) +# switch_firmware_enabled: true +# switch_firmware_target: "7.x.y" # EDIT to the version pinned in Phase 0.3 diff --git a/play_switch.yml b/play_switch.yml new file mode 100644 index 0000000..393764a --- /dev/null +++ b/play_switch.yml @@ -0,0 +1,6 @@ +--- +- name: Configure MikroTik switches (day-2, key auth) + hosts: mikrotik + gather_facts: false + roles: + - makerfloss.mikrotik_switch diff --git a/roles/makerfloss.mikrotik_switch/defaults/main.yml b/roles/makerfloss.mikrotik_switch/defaults/main.yml new file mode 100644 index 0000000..fba749b --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/defaults/main.yml @@ -0,0 +1,38 @@ +--- +# ----- Identity / management ----- +switch_identity_name: "{{ inventory_hostname }}" +switch_mgmt_vlan_id: 99 +switch_mgmt_address: "192.168.88.1/24" # PLACEHOLDER — override in host_vars +switch_mgmt_gateway: "192.168.88.254" # PLACEHOLDER — override in host_vars +switch_dns_servers: "192.168.88.254" +switch_ntp_servers: "192.168.88.254" + +# Services to disable for hardening (winbox kept on by default for recovery) +switch_disabled_services: + - telnet + - ftp + - www + - www-ssl + - api + - api-ssl +switch_ssh_port: 22 + +# ----- Users ----- +switch_admin_user: "sjat" +switch_admin_group: "full" +switch_admin_ssh_pubkey_file: "~/.ssh/id_ed25519.pub" +switch_disable_default_admin: true + +# ----- VLAN / bridge / ports (PLACEHOLDER example) ----- +# Real topology is defined in host_vars/.yml. +switch_bridge_name: "bridge" +switch_vlans: + - {id: 99, name: "mgmt"} + - {id: 10, name: "members"} +switch_bridge_ports: + # ether1..ether8 = 2.5GbE access ports; sfp-sfpplus1/2 = 10G uplinks + - {interface: "ether1", pvid: 10, mode: access} + - {interface: "sfp-sfpplus1", pvid: 1, mode: trunk, tagged_vlans: [99, 10]} + +# ----- Firmware ----- +switch_firmware_target: "" # set in host_vars when opting into upgrades diff --git a/roles/makerfloss.mikrotik_switch/meta/main.yml b/roles/makerfloss.mikrotik_switch/meta/main.yml new file mode 100644 index 0000000..39c4864 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/meta/main.yml @@ -0,0 +1,10 @@ +--- +galaxy_info: + role_name: mikrotik_switch + namespace: makerfloss + author: sjat + description: Configure a MikroTik RouterOS switch (CRS310) over SSH. + license: MIT + min_ansible_version: "2.17" + platforms: [] +dependencies: [] diff --git a/roles/makerfloss.mikrotik_switch/tasks/backup.yml b/roles/makerfloss.mikrotik_switch/tasks/backup.yml new file mode 100644 index 0000000..fe0a9a3 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/backup.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder + ansible.builtin.debug: + msg: "not yet implemented" diff --git a/roles/makerfloss.mikrotik_switch/tasks/firmware.yml b/roles/makerfloss.mikrotik_switch/tasks/firmware.yml new file mode 100644 index 0000000..fe0a9a3 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/firmware.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder + ansible.builtin.debug: + msg: "not yet implemented" diff --git a/roles/makerfloss.mikrotik_switch/tasks/identity.yml b/roles/makerfloss.mikrotik_switch/tasks/identity.yml new file mode 100644 index 0000000..fe0a9a3 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/identity.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder + ansible.builtin.debug: + msg: "not yet implemented" diff --git a/roles/makerfloss.mikrotik_switch/tasks/main.yml b/roles/makerfloss.mikrotik_switch/tasks/main.yml new file mode 100644 index 0000000..101959f --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/main.yml @@ -0,0 +1,25 @@ +--- +- name: Identity, management and services + ansible.builtin.import_tasks: identity.yml + when: switch_identity_enabled | bool + tags: [identity] + +- name: Users and SSH keys + ansible.builtin.import_tasks: users.yml + when: switch_users_enabled | bool + tags: [users] + +- name: VLANs, bridge and ports + ansible.builtin.import_tasks: vlans.yml + when: switch_vlans_enabled | bool + tags: [vlans] + +- name: Backup configuration + ansible.builtin.import_tasks: backup.yml + when: switch_backup_enabled | bool + tags: [backup] + +- name: Firmware upgrade + ansible.builtin.import_tasks: firmware.yml + when: switch_firmware_enabled | bool + tags: [firmware] diff --git a/roles/makerfloss.mikrotik_switch/tasks/users.yml b/roles/makerfloss.mikrotik_switch/tasks/users.yml new file mode 100644 index 0000000..fe0a9a3 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/users.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder + ansible.builtin.debug: + msg: "not yet implemented" diff --git a/roles/makerfloss.mikrotik_switch/tasks/vlans.yml b/roles/makerfloss.mikrotik_switch/tasks/vlans.yml new file mode 100644 index 0000000..fe0a9a3 --- /dev/null +++ b/roles/makerfloss.mikrotik_switch/tasks/vlans.yml @@ -0,0 +1,4 @@ +--- +- name: Placeholder + ansible.builtin.debug: + msg: "not yet implemented" From 0721ecc34c5018790f81d55034063d9f9c0675f7 Mon Sep 17 00:00:00 2001 From: sjat Date: Sun, 7 Jun 2026 08:38:23 +0200 Subject: [PATCH 6/6] docs(plan): carry-over notes from skeleton code review Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-07-mikrotik-crs310-ansible.md | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/superpowers/plans/2026-06-07-mikrotik-crs310-ansible.md b/docs/superpowers/plans/2026-06-07-mikrotik-crs310-ansible.md index 6a7656a..bba90b3 100644 --- a/docs/superpowers/plans/2026-06-07-mikrotik-crs310-ansible.md +++ b/docs/superpowers/plans/2026-06-07-mikrotik-crs310-ansible.md @@ -922,3 +922,25 @@ Expected: repo populated on `forgejo.makerfloss.eu`. - **RouterOS version drift:** exact CLI syntax (NTP `servers=` property, `ssh-keys/import` path) is RouterOS-7 specific; verify against the pinned version from Phase 0.3 and adjust. - **`net_put`/`net_get` over `network_cli`:** depends on SCP being available on the RouterOS SSH service; if it fails, fall back to importing the key by pasting its contents via `/user/ssh-keys/...` or enabling SCP. - **`changed_when: false`** is used widely because the `command` module can't detect RouterOS state changes; idempotency comes from the `:if [find]` guards. Revisit if you want accurate change reporting (parse command output). + +## Carry-over notes from the skeleton code review (Tasks 1–3, done 2026-06-07) + +The no-device tasks (1–3) are implemented, reviewed, and committed on branch +`feat/initial-scaffolding`. The code-quality review of the role skeleton raised these +points to handle WHEN the device task files (Tasks 5–9) are written: + +- **`switch_ssh_port` (default 22):** the identity task will *set* the SSH port. If the + device was manually moved to a non-standard port before Ansible manages it, the first + run resets it to 22 and the connection drops. Confirm the live port matches before the + identity task runs, or override `switch_ssh_port` in host_vars. +- **`switch_bridge_name` / `switch_admin_group`:** these default to the CRS310 factory + values (`bridge` / `full`) and are NOT overridden in host_vars. Correct for this one + device; if the bridge/group name ever differs, the VLAN and users tasks silently target + the wrong object. Add explicit host_vars overrides if a second device is ever onboarded. +- **Trunk `pvid: 1` (sfp-sfpplus1):** untagged frames on the uplink land in VLAN 1. In a + hardened VLAN design VLAN 1 is usually unused — when writing `vlans.yml`, decide + deliberately whether the trunk should accept untagged traffic at all, and comment intent. +- **host_vars `# EDIT:` placeholders:** `switch_mgmt_address/gateway/dns/ntp` in + `host_vars/crs310-maker.yml` hold plausible `10.0.99.x` placeholders. Replace with the + real values from the field guide (Step 7) and remove the `# EDIT` comments so it's + unambiguous they were updated.