# Provision a drone on the bench: identity, peer trust, WireGuard, data plane.
# Idempotent; never runs over the mission radio (bench network only).
---
- name: Provision drone data plane
  hosts: drones
  become: true
  vars:
    swarm_user: swarm
    swarm_home: /opt/swarm
    forced_command: "{{ swarm_home }}/bin/peer-query.sh"

  tasks:
    - name: Create swarm service user
      ansible.builtin.user:
        name: "{{ swarm_user }}"
        home: "{{ swarm_home }}"
        shell: /usr/sbin/nologin
        system: true

    # --- 1. Identity -----------------------------------------------------
    - name: Generate drone ed25519 identity key
      community.crypto.openssh_keypair:
        path: "{{ swarm_home }}/.ssh/id_ed25519"
        type: ed25519
        comment: "{{ inventory_hostname }}"
        owner: "{{ swarm_user }}"
        mode: "0600"
      register: identity

    - name: Collect fleet public keys
      ansible.builtin.slurp:
        src: "{{ swarm_home }}/.ssh/id_ed25519.pub"
      register: pubkey

    # --- 2. Peer trust: forced command only ------------------------------
    # Every peer may connect, but the only thing it can execute is the
    # read-only SQL wrapper (docs/04). No shell, no forwarding, no pty.
    - name: Authorize fleet peers with read-only forced command
      ansible.posix.authorized_key:
        user: "{{ swarm_user }}"
        key: "{{ hostvars[item].pubkey.content | b64decode }}"
        key_options: >-
          command="{{ forced_command }}",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty
      loop: "{{ groups['drones'] | difference([inventory_hostname]) }}"
      when: hostvars[item].pubkey is defined

    - name: Install peer query wrapper
      ansible.builtin.copy:
        dest: "{{ forced_command }}"
        mode: "0755"
        content: |
          #!/bin/sh
          # Forced command: read-only DuckDB SQL over the sealed lake.
          # SSH_ORIGINAL_COMMAND carries the statement; the gate rejects
          # anything that is not a single read statement.
          exec {{ swarm_home }}/bin/sql-gate --read-only --data /data "$SSH_ORIGINAL_COMMAND"

    # --- 3. WireGuard swarm plane ----------------------------------------
    - name: Render WireGuard configuration
      ansible.builtin.template:
        src: templates/wg0.conf.j2
        dest: /etc/wireguard/wg0.conf
        mode: "0600"
      notify: Restart wireguard

    - name: Enable WireGuard interface
      ansible.builtin.systemd:
        name: wg-quick@wg0
        enabled: true
        state: started

    # --- 4. Data plane from the fleet release manifest --------------------
    - name: Read fleet release manifest
      ansible.builtin.include_vars:
        file: "{{ fleet_manifest }}"
        name: manifest

    - name: Lay down Compose bundle for release {{ manifest.release }}
      ansible.builtin.unarchive:
        src: "{{ playbook_dir }}/../../.tmp/{{ manifest.artifacts[0].name }}"
        dest: "{{ swarm_home }}/release"
        owner: "{{ swarm_user }}"

    - name: Enable data plane unit
      ansible.builtin.template:
        src: templates/swarm-data-plane.service.j2
        dest: /etc/systemd/system/swarm-data-plane.service
        mode: "0644"
      notify: Restart data plane

  handlers:
    - name: Restart wireguard
      ansible.builtin.systemd:
        name: wg-quick@wg0
        state: restarted

    - name: Restart data plane
      ansible.builtin.systemd:
        name: swarm-data-plane
        state: restarted
        daemon_reload: true
