Back to downloads

Running Cosinium and Cosineq

Recipes for running the Cosinium image in a VM or container, and for running the Cosineq mesh images. Adapt the commands to your deployment scenario. You can download the images from the downloads page.

VMware

VMware Workstation and Fusion

Convert the qcow2 image to a VMDK and attach it as an existing disk.

qemu-img convert -f qcow2 -O vmdk -o adapter_type=lsilogic cosos-efi-x86_64.qcow2 cosos-efi-x86_64.vmdk
  1. Create a new VM with a custom configuration. Guest OS: Other Linux 6.x kernel 64-bit.
  2. Choose Use an existing virtual disk and select the VMDK.
  3. Firmware: UEFI (the image also boots BIOS) - disable Secure Boot. Give it at least 2 vCPU and 2 GB RAM.
  4. Add a second network adapter. First adapter = WAN (bridged or NAT), second = LAN (host-only or a LAN segment).
  5. Add a serial port to a file or named pipe if you want the console log.

On Apple silicon use the aarch64 image with Fusion.

VMware ESXi (x86_64)

ESXi does not accept the Workstation VMDK format as is. Convert to a stream optimized VMDK on your system with qemu image tool, upload it to a datastore and set it as the thin disk for your VM.

# on your workstation
qemu-img convert -f qcow2 -O vmdk -o subformat=streamOptimized,adapter_type=lsilogic \
    cosos-efi-x86_64.qcow2 cosos-esxi-upload.vmdk

# upload cosos-esxi-upload.vmdk to the datastore (web UI or scp), then on the ESXi shell
cd /vmfs/volumes/datastore1/cosos
vmkfstools -i cosos-esxi-upload.vmdk -d thin cosos-efi-x86_64.vmdk
rm cosos-esxi-upload.vmdk
  1. Create a VM: guest Other Linux (64-bit), boot option UEFI (disable secure boot for the VM), 2 vCPU, 2 GB RAM.
  2. Remove the default hard disk and add an existing hard disk: cosos-efi-x86_64.vmdk. Controller: LSI Logic Parallel or SAS.
  3. Add two network adapters (VMXNET3 or E1000). First = WAN port group, second = LAN port group.
  4. For a LAN port group that serves real clients, allow promiscuous mode and forged transmits on that port group if you bridge or run VRRP. If you want to configure tagged VLANs in the VM, then ensure the LAN interface's port group is configured with VLAN ID 4095.

Conventions


USB flash drive (x86_64)

The qcow2 image is a whole disk, which can be expanded to raw and written a memory stick. Use the x86_64 image, because the aarch64 one is for arm64 machines which we've only tested in a virtual environment. The image works under both UEFI (with Secure Boot off) and legacy BIOS.

1. Expand the qcow2 to a raw image

qemu-img info cosos-efi-x86_64-<rev>.qcow2
qemu-img convert -f qcow2 -O raw cosos-efi-x86_64-<rev>.qcow2 cosos-efi-x86_64.img

qemu-img info prints the virtual size and the raw file will be that size. Current builds report 16 GiB virtual, with Cosinium's data taking 1GiB.

Check what you got:

sfdisk -l cosos-efi-x86_64.img

Expect three partitions: a 1 MiB BIOS boot partition, a 128 MiB EFI system partition and the ext4 root file system. sfdisk also reports that the backup GPT table is not at the end of the device, which is expected - the first boot will repair it.

2. Find the stick

lsblk -o NAME,SIZE,TRAN,TYPE,MODEL
Write to the disk device: Write to /dev/sdX, the disk itself, and not to a partition in it such as /dev/sdX1. Everything on the stick is destroyed. Read the size and the model in the lsblk output and confirm they belong to the the stick before you continue.

3. Write the raw image to the stick

sudo dd if=cosos-efi-x86_64.img of=/dev/sdX bs=4M conv=fsync status=progress
sudo sync

Use a stick of 32 GB or more because the raw file is 16 GiB. A stick that is sold as 16 GB is about 14.9 GiB, which will fall short of the image size. The extra space on flash media is used to hold logs and system telemetry.

You can also use qemu-img to write to the stick in one step, which is a lot faster and needs no raw file on disk:

sudo qemu-img convert -f qcow2 -O raw cosos-efi-x86_64-<rev>.qcow2 /dev/sdX

On macOS the device names differ and the raw node will be faster:

diskutil list
diskutil unmountDisk /dev/diskN
sudo dd if=cosos-efi-x86_64.img of=/dev/rdiskN bs=4m

On Windows use a writer that takes a raw disk image, such as Rufus in DD mode or balenaEtcher.

4. Boot the machine

The kernel will look for the root filesystem by its partition label (cosos-outer).

5. The first boot expands the root filesystem

When booting for the first time, the root partition and its ext5 filesystem will be expanded to the full size of the stick. Monitor the process to ensure that it has completed successfully.

Pick a fast stick. Cosinium keeps its configuration and the inner system images on the root filesystem and writes to them while it runs. A slow stick can potentially cause the system to feel sluggish.

Then follow the verification checklist. The setup webapp is on the LAN address 10.123.123.1.

VM scenarios (QEMU)

1. User-mode networking - quickest, no host setup

Both NICs are NAT'd by qemu. Webapp reachable via port-forward. This setup doesn't support real clients on the LAN.

x86_64

IMG=$(ls -t cosos-qemu*.qcow2 | head -1)

sudo qemu-system-x86_64 -nographic -m 4G -smp 2 \
   -drive file=${IMG},if=virtio \
   -netdev user,id=n0,hostfwd=tcp::12888-:80,hostfwd=tcp::12443-:443 \
   -device virtio-net,netdev=n0 \
   -netdev user,id=n1 \
   -device virtio-net,netdev=n1 \
   -serial mon:stdio -vga std -vnc 0.0.0.0:1

Webapp: http://localhost:12888/.

aarch64

sudo qemu-system-aarch64 -M virt -cpu max -m 4G -smp 2 -nographic \
   -drive if=pflash,format=raw,readonly=on,file=/usr/share/AAVMF/AAVMF_CODE.fd \
   -drive if=pflash,format=raw,file=/tmp/AAVMF_VARS.fd \
   -drive file=${IMG},if=virtio,cache=writeback \
   -netdev user,id=n0,hostfwd=tcp::12443-:443,hostfwd=tcp::12222-:22 \
   -device virtio-net-pci,netdev=n0 \
   -netdev user,id=n1 -device virtio-net-pci,netdev=n1 \
   -serial mon:stdio

AAVMF_VARS.fd is per-VM mutable EFI vars - copy /usr/share/AAVMF/AAVMF_VARS.fd to /tmp/ before first boot.

2. Native-CPU acceleration (KVM)

Add -enable-kvm -cpu host -machine accel=kvm on x86_64; on aarch64 host running aarch64 guest, add -accel kvm -cpu host and drop -cpu max. Everything else is the same as scenario 1.

sudo qemu-system-x86_64 -enable-kvm -cpu host -machine q35,accel=kvm \
   -m 4G -smp 2 -nographic \
   -drive file=${IMG},if=virtio \
   -netdev user,id=n0,hostfwd=tcp::12888-:80 \
   -device virtio-net,netdev=n0 \
   -netdev user,id=n1 -device virtio-net,netdev=n1 \
   -serial mon:stdio -vga std

User must be in the kvm group, or run via sudo.

3. Bridged WAN - cosinium joins your existing LAN as a router

WAN is on a host bridge that contains your uplink NIC; LAN stays user-mode (or use scenario 4 for a real LAN).

One-time host bridge setup (Linux)

# Create br0 and enslave the uplink NIC. Replace enp1s0 with yours.
sudo ip link add br0 type bridge
sudo ip link set enp1s0 master br0
sudo ip link set br0 up
sudo ip link set enp1s0 up

# Move the host's IP onto br0 (or run DHCP on br0):
sudo dhclient br0     # or: sudo ip addr add 192.168.1.50/24 dev br0

Persist with systemd-networkd / NetworkManager / /etc/network/interfaces once you've confirmed it works.

Allow QEMU's bridge helper

# Permit br0 for /usr/lib/qemu/qemu-bridge-helper.
echo 'allow br0' | sudo tee -a /etc/qemu/bridge.conf
sudo chmod u+s /usr/lib/qemu/qemu-bridge-helper

Launch

IMG=$(ls -t dist/cosos-qemu*.qcow2 | head -1)

sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2G -smp 2 -nographic \
   -drive file=${IMG},if=virtio \
   -netdev bridge,id=n0,br=br0 \
   -device virtio-net,netdev=n0,mac=52:54:00:12:34:56 \
   -netdev user,id=n1 -device virtio-net,netdev=n1 \
   -serial mon:stdio -vga std -vnc 0.0.0.0:1

Cosinium's WAN port will DHCP off your upstream router.

4. Two-NIC router with a real LAN (bridged WAN + tap LAN)

WAN bridged to br0 (as in scenario 3); LAN attached to a second bridge br-lan that you can use for your LAN clients to connect to.

Host setup

sudo ip link add br-lan type bridge
sudo ip link set br-lan up

# Option A: enslave a second physical NIC (clients plug into it)
sudo ip link set enp2s0 master br-lan
sudo ip link set enp2s0 up

# Option B: create a host tap so the host can talk to the LAN
sudo ip tuntap add lan-host mode tap user "$USER"
sudo ip link set lan-host master br-lan
sudo ip link set lan-host up
sudo ip addr add 10.123.123.50/24 dev lan-host   # host's address on cosinium-LAN

echo 'allow br-lan' | sudo tee -a /etc/qemu/bridge.conf

Launch

sudo qemu-system-x86_64 -enable-kvm -cpu host -m 2G -smp 2 -nographic \
   -drive file=${IMG},if=virtio \
   -netdev bridge,id=n0,br=br0     -device virtio-net,netdev=n0,mac=52:54:00:12:34:56 \
   -netdev bridge,id=n1,br=br-lan  -device virtio-net,netdev=n1,mac=52:54:00:12:34:57 \
   -serial mon:stdio -vga std -vnc 0.0.0.0:1

From the host (with Option B), browse to http://10.123.123.1/ to hit cosinium's LAN-side setup webapp.


Container scenarios (Podman, rootful)

Import the outer image first:

sudo podman import \
    --change 'ENTRYPOINT ["/entrypoint.sh"]' \
    --change 'EXPOSE 22' --change 'EXPOSE 80' --change 'EXPOSE 443' \
    cosos-qemu-virt-<arch>-<version>.tar.gz \
    cosinium:latest

Cosinium's config is stored in the container under /var/db/cosinium - mount it as a named volume if you want it to be persistent across restarts.

5. Sandbox trial - two podman networks, no host integration

Cosinium runs on isolated bridges. Useful for testing the features of Cosinium; this cannot be used to serve real clients.

sudo podman network create --disable-dns \
    --subnet 10.99.0.0/24 --gateway 10.99.0.254 coswan
sudo podman network create --disable-dns \
    --subnet 10.99.1.0/24 --gateway 10.99.1.254 coslan

sudo podman run -d --name cosinium --hostname cosinium \
    --privileged --restart=always \
    --tmpfs /run --tmpfs /tmp \
    --network coswan:ip=10.99.0.2 \
    --network coslan:ip=10.99.1.1 \
    -v cosinium-data:/var/db/cosinium \
    cosinium:latest

Watch boot: sudo podman logs -f cosinium. The first --network becomes eth0 (WAN); the second becomes eth1 (LAN).

6. Real gateway - macvlan per side (recommended)

Dedicate one physical NIC to WAN and another to LAN. Each side gets its own MAC on the real NIC. Clients on the LAN segment use Cosinium as their gateway via 10.123.123.1 (reconfigurable).

# WAN macvlan: parent is your uplink NIC. Cosinium acquires an IP
# via DHCP from the upstream router on this segment.
sudo podman network create -d macvlan -o parent=enp1s0 \
    --subnet 192.168.1.0/24 --gateway 192.168.1.1 cos-wan

# LAN macvlan: parent is the NIC your clients plug into (or a host
# bridge with clients hanging off it). No subnet/gateway  -  cosinium
# will configure 10.123.123.1/24 itself.
sudo podman network create -d macvlan -o parent=enp2s0 cos-lan

sudo podman run -d --name cosinium --hostname cosinium \
    --privileged --restart=always \
    --tmpfs /run --tmpfs /tmp \
    --network cos-wan \
    --network cos-lan \
    -v cosinium-data:/var/db/cosinium \
    cosinium:latest
Caveats.
  • The host itself cannot talk to the macvlan IP it owns the parent of because of the Linux kernel limitation. Manage cosinium from a different machine on the LAN, or add a macvlan shim on the host: ip link add cos-shim link enp2s0 type macvlan mode bridge.
  • WiFi parents typically refuse macvlan, so use a wired uplink.

7. Real gateway - --network host

Simplest, but Cosinium and the host share the same network namespace: they fight over interfaces, addresses, and netfilter rules. Use only on a host whose sole purpose is running Cosinium. Disable any network and firewall management on the host to let Cosinium control them.

sudo podman run -d --name cosinium --hostname cosinium \
    --privileged --restart=always \
    --tmpfs /run --tmpfs /tmp \
    --network host \
    -v cosinium-data:/var/db/cosinium \
    cosinium:latest

Before launching: stop the host's NetworkManager / systemd-networkd / dhcpcd on the NICs cosinium will manage, and flush any addresses / firewall rules left behind.

8. Real gateway - host bridges, container on podman bridge driver

You pre-configure br-wan and br-lan on the host (enslaving physical NICs), and attach Cosinium via standard podman bridge networks pinned to those bridges. More moving parts than scenario 6, but the host retains a normal addressable presence on each interface.

# Host: build the bridges and enslave the NICs.
sudo ip link add br-wan type bridge && sudo ip link set br-wan up
sudo ip link set enp1s0 master br-wan && sudo ip link set enp1s0 up
sudo dhclient br-wan        # host keeps its uplink address

sudo ip link add br-lan type bridge && sudo ip link set br-lan up
sudo ip link set enp2s0 master br-lan && sudo ip link set enp2s0 up
# Don't give br-lan an IP  -  Cosinium owns 10.123.123.1.

# Podman networks bound to those host bridges.
sudo podman network create --disable-dns \
    -o parent=br-wan --subnet 192.168.1.0/24 --gateway 192.168.1.1 cos-wan
sudo podman network create --disable-dns \
    -o parent=br-lan cos-lan

sudo podman run -d --name cosinium --hostname cosinium \
    --privileged --restart=always \
    --tmpfs /run --tmpfs /tmp \
    --network cos-wan \
    --network cos-lan \
    -v cosinium-data:/var/db/cosinium \
    cosinium:latest

Verification checklist

After any container-based scenario:

# Container: tail the boot.
sudo podman logs -f cosinium      # expect "[cosinium] OpenRC up." within ~60s

# Webapp reachable on the LAN address from a LAN client:
curl -k https://10.123.123.1/

# WAN got DHCP (gateway-mode scenarios):
sudo podman exec cosinium ip -4 addr show eth0
sudo podman exec cosinium ip route

First-login admin credentials are admin:cosinium - change them in System -> Accounts before exposing the box.

For console access, the username is admin (with a cosinium alias). The admin acount in the UI is mapped to the cosinium user on the host.


Cosineq mesh images

Cosineq mesh images come in two forms, both of which are OCI formats and do not run Cosinium OS. The controller image contains the mesh controller plus quicn, quicnctl, quicn-stun and quicn-relay. The node image holds the same services without the mesh-controller engine; it only has a STUN or relay server that is managed by a remote controller.

quicn, the post-quantum mesh VPN client, is contained in both image types and can be configured using the webui within the image. quicnctl is a basic TUI that can also be used to enrol quicn to a Cosineq controller.

Each image is published three ways: an x86_64 archive, an aarch64 archive, and a multi-architecture archive with both inside. Take the archive that matches your host unless you are pushing the image into a registry that serves both.

1. Load an OCI archive

podman load -i cosineq-controller-<tag>-x86_64.oci.tar

The load prints the image name, which is localhost/cosineq-controller:<tag>-x86_64. The multi-architecture archive loads the same way and podman picks the architecture of the host.

For Docker, convert the archive with skopeo:

skopeo copy oci-archive:cosineq-controller-<tag>-x86_64.oci.tar \
    docker-daemon:cosineq-controller:<tag>

To push it to your own registry:

skopeo copy --all oci-archive:cosineq-controller-<tag>.oci.tar \
    docker://registry.example.com/cosineq-controller:<tag>

2. Run the controller

Rootful, with the kernel tun device so quicn can build real routes:

podman run -d --name cosineq \
  --cap-add NET_ADMIN --device /dev/net/tun \
  -e COSINEQ_WEB_LISTEN=0.0.0.0:8080 -p 8080:8080 \
  -p 4790:4790/udp -p 4791:4791/udp -p 3478:3478/udp \
  -p 4792:4792/udp -p 4793:4793/udp \
  -v cosineq-data:/var/lib/cosineq -v cosineq-log:/var/log \
  --restart unless-stopped \
  localhost/cosineq-controller:<tag>-x86_64

Open http://<host>:8080/ and log in as admin with password cosinium. Password reset is required on first login.

The web listener defaults to 127.0.0.1:8080 inside the container, which -p cannot reach on a bridge network. Set COSINEQ_WEB_LISTEN=0.0.0.0:8080 as above, or run with --network host. Do not expose or route it to the internet.

Environment

VariableDefaultMeaning
COSINEQ_WEB_LISTEN127.0.0.1:8080web listener host and port
COSINEQ_WEB_TLSfalseTLS on the listener; a self-signed pair is minted on the data volume when no certificate is given
COSINEQ_WEB_TLS_CERT, COSINEQ_WEB_TLS_KEYPEM pair for the listener
COSINEQ_LOG_LEVELinfodebug, info or warning
COSINEQ_DATA_ROOT/var/lib/cosineqthe data volume
QUICN_MODEautoauto, kernel or userstack; auto uses the kernel tun only with real NET_ADMIN and /dev/net/tun

The same values live in coscontrol.toml on the data volume, but the environment variables override them.

3. Run a node and register it

A node image serves a controller as a STUN or relay server. Run it the same way:

podman run -d --name cosineq-node \
  --cap-add NET_ADMIN --device /dev/net/tun \
  -e COSINEQ_WEB_LISTEN=0.0.0.0:8080 -p 8080:8080 \
  -p 3478:3478/udp -p 4792:4792/udp -p 4793:4793/udp \
  -v cosineq-node-data:/var/lib/cosineq -v cosineq-node-log:/var/log \
  --restart unless-stopped \
  localhost/cosineq-node:<tag>-x86_64

Then join it to a controller:

  1. On the node, open the QuicN STUN or QuicN Relay page and create an instance with a name, a port and a public endpoint. The node creates its own identity.
  2. Download the node bundle from that page.
  3. On the controller, open the Subordinates tab, add a STUN or relay server and import the bundle.
  4. Download the controller key from the same tab, paste it into the node instance and start the instance. A relay will not until the key is set.

A controller can also generate a deployment bundle for a node. Importing that bundle on the node will let it start right away.