Operator runbook for build one customer. Four phase, run top to bottom in order: AssemblyPrepareSetupInstall. Admin steps (Freshdesk ticket, quote, deliver, bill) bracket the technical phases and get called out inside Prepare + Install. Every phase share one config procedure: Customer config workflow (inside Prepare).

1. Phase 1 - Assembly

Trigger: quote accepted, hardware on the way. End state: all parts on bench, verified, cabled, MikroTik flashed with NETSetup base config. This phase is bench work - the live internet line is wired in Setup.

  1. Order hardware. Go to osisaprod repository Liberator folder files, order from suppliers. See Beschaffung + Alltron Artikeldaten.

  2. Check BOM on arrival. Boxes arrive - tick every part off the bill of materials before unboxing.

    NETSetup Liberator BOM - TV box
    Figure 1. BOM - parts still boxed. Verify all present.

    For a network-single build the parts that live inside the Liberator: power multi-plug, ethernet cable, fibre patch cable, MikroTik, modem. The TV box is a separate / optional add-on (not part of the Liberator).

  3. Unbox + lay out. Unbox everything, lay it out, confirm nothing missing or damaged.

    Unboxed - MikroTik hAP ax2 center
    Figure 2. All parts unboxed for inventory.
  4. Identify the fibre patch. The bundled patch is LC/APC-SC/APC 9/125 single-mode (FS, P/N SMLCSX) - this is the XGS-PON cable for the Nokia ONT. A BX line needs a different patch - check osisa prod liberator.xlsx before ordering.

    FS Fiber Patch Cable 10M 9/125 single mode LC/APC-SC/APC
    Figure 3. Fibre patch cable - LC/APC-SC/APC, XGS-PON only.
  5. Identify the modem. Nokia ONT, model XS-010X-R, 12V DC. Note the MAC ID + S/N off the underside label (S/N also drives line provisioning at the ISP).

    Nokia XS-010X-R label - MAC ID
    Figure 4. Nokia ONT underside - model, MAC ID, S/N.
  6. Identify the MikroTik. hAP ax2 (C52iG-5HaxD2HaxD-TC). The sticker carries User: admin, the factory Password, the Wifi key, and both MAC addresses (E01 = ether, W01 = wifi). The sticker password is the --admin-password you pass to flash-mikrotik.

    MikroTik hAP ax2 sticker - admin password
    Figure 5. MikroTik hAP ax2 sticker - admin password + wifi key + MACs.
  7. Identify the TV box (optional). TEP Replay+ (TIPc9x, Tele Alpin AG), HDMI, 5V DC. Note the MAC off the underside - needed for TEP activation (see Setup).

    TEP Replay+ TIPc9x TV box label - MAC C44EAC21A040
    Figure 6. TEP Replay+ TV box underside - model + MAC.
  8. Flash the MikroTik. Cable the PC to ether2-5, run NETSetup.exe flash-mikrotik --admin-password <sticker-pw>. This loads the NETSetup base config: bridge LAN 10.0.0.1/24 + dhcp-server, plus dual-WAN auto-detect (dhcp-client on untagged ether1 AND tagged ether1-vlan10). See flash-mikrotik in Mikrotik.adoc. The manual winbox equivalent (verify / fallback for non-flashed boxes) is in Setup.

2. Phase 2 - Prepare

Trigger: customer reach out (phone 061 763 00 00, support@osisa.com, or web). End state: the customer config is written, published to Dropbox, and committed - ready to drive install.

  1. Create ticket in Freshdesk. Every new customer start here. Open osisa Freshdesk, new ticket, fill mandatory fields, note ticket number - it tag all later artifacts. See Ticketverwaltung in Freshdesk.

  2. Ist-Soll analysis. Capture current mode + future mode of customer network. See Offertprozess and Netzwerk Ist-Soll-Analyse.

    1. No current environment, from scratch

    2. Already an existing environment

  3. Quote. Build quote from future mode, send, get acceptance. Loop on customer feedback. See Offertprozess. Acceptance triggers Assembly (order hardware).

  4. Document the future mode. First write the target network in the customer documentation - it drives config + install. Copy Kunden-Netzwerkdokumentation template (Staudt scheme), fill per customer (Kontakte, Remote-Zugriff, Netzwerk, Firewall, Domains, E-Mail, Drucker, Server und Computer). Render with render-customer-doc.ps1 (or asciidoctor) to PDF/DOCX.

  5. Create + publish the customer config. From the documented future mode write Customers/<Customer>.cs (employees, devices, serials), regenerate JSON, publish to Dropbox, commit. Full procedure:

    1. Customer configs live in NETSetup Repository Customers/ as .NET 10 file-based .cs apps. Each one builds a Company, runs the translator, emits <Customer>.json next to itself. The JSON is what NETSetup reads at install time, keyed off the machine serial. Use the netsetup-customer-config skill; override examples in Customers/README.md.

    2. Checkout latest. Pull current HEAD so you start from the same source other devs see.

      git clone https://github.com/osisa/netsetup.git
      cd netsetup
      git checkout develop
      git pull
    3. Edit Customers/<Customer>.cs. Add/change .AddDevice(name, type).WithSerialNumber(…​) for a new machine. Add .AddEmployee(…​) for a new user. Override examples (admin password, fixed IP, public URI, serial swap) live in Customers/README.md. Template:

      #:project ../src/NETSetup/NETSetup.csproj
      #:package osisa.UserManagement.Contracts
      
      using System.Runtime.CompilerServices;
      using System.Text.Json;
      
      using Nuke.Common.IO;
      
      using osisa.FileProviders.Physical;
      using osisa.IO.Contracts;
      
      using NETSetup.Config.Translators;
      using NETSetup.Entities;
      using NETSetup.Extensions;
      
      using osisa.Enterprise.Contracts;
      using osisa.Enterprise.Entities;
      using osisa.Security.Contracts;
      using osisa.SystemManagement.Contracts.Elements;
      using osisa.SystemManagement.Contracts.Names;
      using osisa.SystemManagement.Entities;
      using osisa.UserManagement.Contracts;
      
      var company = Company.CreateBuilder("laufentaler.org", "CH", "Roeschenz")
      // Dyndns (homelab run): No-IP DDNS Key. Updating all.ddnskey.com pushes every hostname
      // assigned to the key (laufentaler.ddns.net must be in the key's group). ddns-updater
      // on the dns VM learns the WAN IP via curl echo and pushes it here.
      // WARNING: live DDNS-Key creds - do NOT commit/push this file; revert to a placeholder
      // before commit or rotate the key in No-IP afterward.
      .AddDomainProvider(
      "noip",
      "35BWoCUF7nRS".ToSecretString(),
      new[] { "all" },
      username: "9pvx4ey",
      secretKey: "password",
      domain: "ddnskey.com")
      
      .AddDevice("Liberator", DeviceTypes.Server) // Root Server
      .WithSerialNumber("lib")
      .WithDescription("Liberator")
      
      .AddDevice("Power-1", DeviceTypes.Server)
      .WithSerialNumber("power-1")
      .WithDescription("Power Station")
      
      // MikroTik hAP ax2 (WAN-facing AP/router for the Liberator stack). DeviceType
      // AccessPoint -> NETSetupMikrotik. Description carries the admin password. Presence
      // seeds the 'wifi' user + drives the Mikrotik converge. OPNsense itself is gated on
      // the Description="Liberator" host (already present), not on this device.
      .AddDevice("Mikrotik", DeviceTypes.AccessPoint)
      .WithSerialNumber("mt")
      .WithDescription("MikrotikAdmin4242$")
      
      // OPNsense / Nextcloud / Mail / Dns / PMG VMs are now injected implicitly by
      // LiberatorWithWinDCTranslator on the single Description="Liberator" host. Do NOT
      // re-add explicit AddDevice blocks for them - the translator throws on conflict.
      // Implicit VMIDs (10xx range, Proxmox-safe): OPNSense=1001, Dns=1002, Nextcloud=1020, Mail=1090, PMG=1091.
      
      .AddDevice("DC", DeviceTypes.Server)
      .WithSerialNumber("dc")
      .WithDescription("Windows Server")
      
      .AddDevice("DefaultClient1", DeviceTypes.Client)
      .WithSerialNumber("client1")
      
      .AddDevice("DefaultClient2", DeviceTypes.Client)
      .WithSerialNumber("client2")
      
      
      .AddTask(CommonTasks.Support)
      .WithDevice("osisasupport", DeviceTypes.Software)
      
      .AddTask(CommonTasks.Office)
      .WithDevice("office365netsetup", DeviceTypes.Software)
      
      
      .AddOrganizationalUnit("local")
      .AddEmployee("A")
      .HasTask(CommonTasks.Support)
      .HasTask(CommonTasks.Office)
      .UsesDevice((DeviceName)"DefaultClient1")
      .UsesDevice((DeviceName)"DefaultClient2")
      
      .AddEmployee("B")
      .HasTask(CommonTasks.Support)
      .UsesDevice((DeviceName)"DefaultClient1")
      .UsesDevice((DeviceName)"DefaultClient2")
      
      .Build();
      
      // --- Translate to Config ---
      // IP scheme = per-OU 10.X.0.0/24 auto (server .200-.240, client/DHCP .1-.199, gateway .254).
      // Test bench leaves GatewayIP unset; VirtualHyperVTestBase fills it from the upstream NIC.
      var translator = new LiberatorWithWinDCTranslator(company);
      translator.Translate();
      var config = translator.Config;
      
      // Outbound relay via Brevo transactional SMTP (587, SASL). Chosen over M365 SMTP AUTH:
      // M365 client submission only sends as the ONE authenticated mailbox, so on-prem a@/b@
      // bounce with "554 5.2.252 SendAsDenied". Brevo sends as any address on the authenticated
      // domain (no SendAs wall). See docs/dyndns-domain-setup.adoc -> "Mail Relay (Outbound
      // Smarthost)" Method T. The SASL Login is the generated <id>@smtp-brevo.com value from
      // Brevo's SMTP page, NOT the account email (account email -> 535 auth failed).
      // !!! LIVE CREDS - DO NOT COMMIT. Rotate the Brevo SMTP key or swap to a placeholder
      // before committing this file (same hazard as the live No-IP DDNS-Key creds below). !!!
      config.AddMailRelay(
          "smtp-relay.brevo.com",
          "ac2c7d001@smtp-brevo.com",
          "xsmtpsib-d71a6a436ab4915d4b1ac3c1c2746a135f3ed3b42383f650f9e55ce4e4f42cea-d1YXzkaw8HYX64a8".ToSecretString());
       // Stub upstream gateway so ToProxmoxAutoInstall has a non-None GatewayIP.
       // VirtualHyperVTestBase overrides this with the real test-bench upstream.
       // Probably is this gateway override wrong
       //config.GatewayIP = IP.Parse("10.0.0.1");
      
      config.ThrowIfNamesAreNotValid();
      
      // --- Output ---
      var json = JsonSerializer.Serialize(config, NETSetupConfig.JsonSerializerOptions);
      Console.WriteLine(json);
      
      AbsolutePath scriptDir = (AbsolutePath)GetScriptDir();
      var fileOp = new PhysicalFileOperator(scriptDir, Microsoft.Extensions.Logging.Abstractions.NullLogger<PhysicalFileOperator>.Instance);
      fileOp.WriteAllTextAsync("Customer.json", json, CancellationToken.None).Wait();
      Console.Error.WriteLine($"Written to {scriptDir / "Customer.json"}");
      
      static string GetScriptDir([CallerFilePath] string? path = null)
          => Path.GetDirectoryName(path)!;
    4. Regenerate JSON. Run the file-based app directly. Writes <Customer>.json next to the .cs, prints same JSON to stdout.

      dotnet run Customers\<Customer>.cs

      Rebuild all at once:

      Customers\_rebuild-all.cmd
    5. Publish to production. Production JSONs live in the osisa Dropbox at %USERPROFILE%\osisa Dropbox\NETSetup\BOOT\Config\. Copy the regenerated <Customer>.json into that folder. NETSetup installs pull the config from there (synced to the USB SYSTEM partition and baked into offline ISOs).

    6. Commit + push. Commit both the .cs change and the regenerated .json so the repo and Dropbox stay in lock-step.

3. Phase 3 - Setup

Trigger: hardware on site, MikroTik flashed (Assembly), config published (Prepare). End state: live internet line up, WAN leasing, MikroTik WiFi configured, TV box activated.

This is the iway internet order into action phase - wire the physical fibre line, bring the modem up, register the line MAC, and verify / fix the MikroTik WAN config by hand.

3.1. Find + plug the OTO slot

  1. Look up the OTO slot. Log into the iway portal (login.iway.ch), go Dienste → Internet → Customer. Read off the line profile: Benutzername (e.g. <id>@dhcp), Aktivierungs-Code, OTO-ID, Steckplatz an der Dose (the slot number), and Anschluss-Technologie (e.g. XGS).

    iway Swisscom BBCS portal - OTO-ID
    Figure 7. iway BBCS portal - line profile, OTO-ID + Steckplatz + activation code.
  2. Plug the fibre into the correct OTO slot. On the wall OTO box, match the OTO-ID label and plug the LC/APC fibre into the exact Steckplatz from the portal (slots are numbered 1-4 - wrong slot = no link).

    Optical Fibre OTO box - green fibre plugged into slot 2
    Figure 8. Wall OTO box - fibre in the slot matching the portal Steckplatz.

3.2. Bring the modem up

  1. Power the modem. Fibre OTO → Nokia ONT, supply power. After a few minutes the LEDs settle: POWER + PON + DATA green, ALARM off = line healthy. PON green = fibre link to the OLT is good; if PON stays off or ALARM lights, the slot/fibre is wrong.

    Nokia ONT LEDs - POWER PON DATA green
    Figure 9. Nokia ONT operational - POWER, PON, DATA green.

3.3. Cable the stack

  1. Cable everything. Nokia ONT → MikroTik ether1 (WAN). LAN clients / Proxmox host on ether2-5. Power via the multi-plug.

    MikroTik hAP ax2 cabled to Nokia modem and clients
    Figure 10. Fully cabled - modem to MikroTik ether1, clients on ether2-5.

3.4. MikroTik WAN config (manual verify / fallback)

Note
flash-mikrotik already sets the dual-WAN auto-detect (dhcp-client on untagged ether1 + tagged ether1-vlan10, both in the WAN list, masquerade out-interface-list=WAN), and netsetup update re-converges it every cycle. The winbox steps below are the manual equivalent - use them to verify on site, troubleshoot, or set up a box that was not flashed by NETSetup. See flash-mikrotik in Mikrotik.adoc.
  1. Connect with WinBox. You need the admin password and the WinBox tool. Join the box WiFi (SSID MikroTik-<xxxxxx>, wifi key off the sticker) or cable to a LAN port, then in WinBox select the box from Neighbors and connect to 192.168.88.1, login admin.

    WinBox Neighbors list - MikroTik at 192.168.88.1
    Figure 11. WinBox - connect via Neighbors to 192.168.88.1.
    MikroTik sticker admin password + Windows WiFi join to MikroTik SSID
    Figure 12. MikroTik sticker creds + joining the box WiFi.
  2. Create VLAN 10 on ether1. iway hands the WAN lease on VLAN 10. In Interfaces → VLAN → New: Name vlan10, Type VLAN, MTU 1500, VLAN ID 10, Interface ether1, comment WAN dhcp.

    WinBox new VLAN interface vlan10 on ether1 VLAN ID 10
    Figure 13. New VLAN10 interface on ether1 for the WAN DHCP lease.
  3. Put vlan10 in the WAN interface-list. In Interfaces → Interface List, add vlan10 to the WAN list (so masquerade / firewall WAN rules apply to it).

    Interface List - LAN bridge
    Figure 14. Interface List - vlan10 added to the WAN list.
  4. Move the DHCP client onto vlan10. In IP → DHCP Client, edit the client and set Interface vlan10 (Use Peer DNS + NTP yes, Add Default Route yes). It goes Status: searching…​ until the line MAC is registered (next step).

    WinBox DHCP Client on vlan10
    Figure 15. DHCP Client bound to vlan10 - searching until MAC registered.

3.5. Register the line MAC (Swisscom physical fibre / XGS-PON)

Warning
A Swisscom physical-fibre line will not lease until the router MAC is registered on the line. This is what clears the Status: searching…​ on the DHCP client.
  1. Register the MikroTik MAC. From the new network, browse to swisscom.ch and register the MikroTik ether1 MAC against the line, using the activation code from the iway portal. Alternative: phone iWay and ask them to register the ether1 MAC.

    swisscom.ch - register MikroTik ether1 MAC
    Figure 16. Register the ether1 MAC at swisscom.ch (or call iWay).

3.6. MikroTik WiFi config

  1. Do NOT use Quick Set. Configure WiFi by hand. Change the SSID + password. For broad device support set WPA2-PSK without the advanced (PMF / WPA3) encryption. Split 2.4 GHz from 5 GHz into separate networks. For old legacy gear, in channel-select pick the 2.4 GHz "G" mode and give it its own SSID - this build named the legacy 2.4 GHz net Liberator-2.

3.7. TV box activation (optional)

  1. Register the TV box MAC with TEP. The TV box needs its MAC registered with TEP - send them an email to activate. During business hours activation takes ~30 min. Multiple TV boxes per line are possible. Each box has a remote with a user-programmable section to power the TV on/off and mimic the original remote’s signal - refer to the TV box manual to program it.

4. Phase 4 - Install

Trigger: line live + config published. End state: machines OS-installed / VMs provisioned, network segmented, customer signed off + billed.

4.1. New Liberator

Trigger: customer need full on-prem stack on one box - Proxmox host running OPNsense + MikroTik router + DNS/Nextcloud/Mail/PMG VMs. End state: VLAN-segmented network, all VMs live, gateway on OPNsense. Full topology + role matrix: Liberator.adoc.

  1. Validate the config. In Customers/<Customer>.cs one of the devices will be the root server, .Description = "Liberator". The translator then auto-injects the VM stack (OPNSense 1001, Dns 1002, Nextcloud 1020, Mail 1090, PMG 1091). Set CustomerDomain, DnsProvider, GatewayIP, WiFi + admin passwords. Regenerate + publish (see Customer config workflow).

  2. Flash the MikroTik. Done in Assembly; manual WAN verify in Setup. See Mikrotik.adoc.

  3. Install Proxmox. Build the auto-install USB, boot the host, let the answer-file install run. See PVE auto-install.

  4. First boot bootstrap. USB first-boot copies the linux-x64 binary to /NETSetup/NETSetup and the systemd unit runs netsetup update. (If first-boot did not run, use the update-proxmox-inplace skill.)

  5. netsetup update provisions everything. On the host every netsetup update cycle: self-updates the binary, runs post-install, syncs nixos.iso, then EnsureCustomerVms creates VMs (OPNsense first, DC next, rest after), converges OPNsense + MikroTik VLANs, rewrites Proxmox bridges, rewires VM NICs to VLANs.

  6. Cutover phase 1 → 2. Additive first, then strip: EnsureGatewayTakeover moves the gateway onto OPNsense LAN. Idempotent, gated on OPNsense LAN ping. See firewall.adoc + Liberator.adoc cutover sequence.

  7. Verify. Nextcloud + Mail serve HTTPS on their subdomains, DNS resolves, VLAN 10/20/250/254 segmented, OPNsense + MikroTik reachable. Re-run netsetup update - it must be a no-op (drift correction).

  8. Document. Record host, VMs, VLANs, IPs, passwords (KeePass refs) in the customer network documentation.

4.2. Power Station

Trigger: extra compute box alongside a Liberator. A device with .Description = "Power Station" is a non-Liberator host: it skips OPNsense + the 4 network-converge helpers entirely. VMs are still provisioned on flat vmbr0 untagged via the same GetOrderedVmsForHost serial-matching path. Install Proxmox + first-boot bootstrap exactly as the Liberator; netsetup update only provisions its VMs (no gateway / VLAN takeover). See Liberator.adoc.

4.3. New client

Trigger: new workstation or laptop for an existing customer (replacement or addition). End state: machine OS-installed, domain-joined, software + user data in place.

  1. Ticket. Open / extend a Freshdesk ticket for the device. osisa Freshdesk.

  2. Add the device to config. In Customers/<Customer>.cs add .AddDevice(name, type).WithSerialNumber(…​); add .AddEmployee(…​) if the user is new. Regenerate + publish (see Customer config workflow).

  3. Backup old machine (replacement only). On the outgoing box run NETSetup backup (or it auto-runs in WinPE before wipe) - mirrors \Users + 3rd-party drivers to the USB.

  4. Boot target via NETSetup USB. Flash the USB if needed (NETSetup.exe create-iso → flash), boot the client from it.

  5. Install. NETSetup.exe install runs the pipeline: OS image → drivers → network → domain join → software (Chocolatey, exact versions from the config).

  6. Restore user data (replacement only). restore pulls the latest backup onto C: (auto-runs in the install flow on the Windows side).

  7. Verify + hand over. Login as the user, confirm software + shares + printers, hand over.

4.4. Deliver + bill

  1. Deliver + sign-off. See Auslieferung - hand over, training material, approval form. Add the machine(s) to the customer network documentation Server und Computer table (name, user, hardware, OS).

  2. Bill + close. Commission bill (Sage), send, on payment notify accounting, close the Freshdesk ticket.