--- - name: Deploy Kottos AI Agent Platform hosts: ubuntu vars: ansible_common_remote_group: "{{ kottos_group | default([]) }}" allow_world_readable_tmpfiles: true handlers: - name: restart kottos become: true ansible.builtin.systemd: name: kottos state: restarted tasks: - name: Check if host has kottos service ansible.builtin.set_fact: has_kottos_service: "{{ 'kottos' in services | default([]) }}" - name: Skip hosts without kottos service ansible.builtin.meta: end_host when: not has_kottos_service - name: Install required packages become: true ansible.builtin.apt: name: - acl - npm - curl state: present update_cache: true - name: Create Kottos group become: true ansible.builtin.group: name: "{{ kottos_group }}" state: present - name: Create Kottos user become: true ansible.builtin.user: name: "{{ kottos_user }}" group: "{{ kottos_group }}" home: "{{ kottos_directory }}" shell: /bin/bash system: true create_home: false - name: Add keeper_user to kottos group become: true ansible.builtin.user: name: "{{ keeper_user }}" groups: "{{ kottos_group }}" append: true - name: Add kottos user to docker group become: true ansible.builtin.user: name: "{{ kottos_user }}" groups: docker append: true notify: restart kottos - name: Reset connection to pick up new group membership ansible.builtin.meta: reset_connection - name: Create Kottos directory become: true ansible.builtin.file: path: "{{ kottos_directory }}" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" state: directory mode: '750' - name: Create vendored Pallas directory become: true ansible.builtin.file: path: "{{ kottos_directory }}/vendor/pallas" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" state: directory mode: '750' - name: Ensure tar is installed for unarchive task become: true ansible.builtin.apt: name: - tar state: present update_cache: true - name: Ensure Python 3.13, venv, dev headers, and ACL are installed become: true ansible.builtin.apt: name: - python3.13 - python3.13-venv - python3.13-dev - acl state: present update_cache: true - name: Transfer and unarchive Kottos release become: true ansible.builtin.unarchive: src: "~/rel/kottos_{{ kottos_rel }}.tar" dest: "{{ kottos_directory }}" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" mode: '550' notify: restart kottos - name: Transfer and unarchive vendored Pallas source become: true ansible.builtin.unarchive: src: "~/rel/pallas_{{ pallas_rel }}.tar" dest: "{{ kottos_directory }}/vendor/pallas" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" mode: '550' notify: restart kottos - name: Rewrite pallas-mcp dependency to use vendored local path become: true ansible.builtin.replace: path: "{{ kottos_directory }}/pyproject.toml" regexp: '"pallas-mcp @ git\+ssh://[^"]+"' replace: '"pallas-mcp @ file://{{ kottos_directory }}/vendor/pallas"' notify: restart kottos - name: Create virtual environment for Kottos (Python 3.13) become: true become_user: "{{ kottos_user }}" ansible.builtin.command: cmd: "python3.13 -m venv {{ kottos_directory }}/.venv/" creates: "{{ kottos_directory }}/.venv/bin/activate" - name: Install wheel and mcp-server-time in virtualenv become: true become_user: "{{ kottos_user }}" ansible.builtin.pip: name: - wheel - mcp-server-time state: latest virtualenv: "{{ kottos_directory }}/.venv" - name: Install Kottos (and its rewritten local pallas-mcp) in virtualenv become: true become_user: "{{ kottos_user }}" ansible.builtin.pip: chdir: "{{ kottos_directory }}" name: . virtualenv: "{{ kottos_directory }}/.venv" virtualenv_command: python3.13 -m venv notify: restart kottos - name: Template agents.yaml become: true ansible.builtin.template: src: agents.yaml.j2 dest: "{{ kottos_directory }}/agents.yaml" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" mode: '640' notify: restart kottos - name: Template fastagent.config.yaml become: true ansible.builtin.template: src: fastagent.config.yaml.j2 dest: "{{ kottos_directory }}/fastagent.config.yaml" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" mode: '640' notify: restart kottos - name: Template fastagent.secrets.yaml become: true ansible.builtin.template: src: fastagent.secrets.yaml.j2 dest: "{{ kottos_directory }}/fastagent.secrets.yaml" owner: "{{ kottos_user }}" group: "{{ kottos_group }}" mode: '640' notify: restart kottos - name: Template systemd service file become: true ansible.builtin.template: src: kottos.service.j2 dest: /etc/systemd/system/kottos.service owner: root group: root mode: '644' notify: restart kottos - name: Enable and start kottos service become: true ansible.builtin.systemd: name: kottos enabled: true state: started daemon_reload: true - name: Flush handlers to restart service before validation ansible.builtin.meta: flush_handlers - name: Validate Kottos registry liveness ansible.builtin.uri: url: "http://localhost:{{ kottos_registry_port }}/live" status_code: 200 return_content: true register: kottos_live retries: 10 delay: 5 until: kottos_live.status == 200