--- # ---------- System setup ---------- - name: Set timezone copy: content: "{{ timezone }}" dest: /etc/timezone owner: root group: root mode: '0644' - name: Link localtime file: src: "/usr/share/zoneinfo/{{ timezone }}" dest: /etc/localtime state: link force: true - name: Install base packages apt: name: "{{ packages }}" state: present update_cache: true cache_valid_time: 3600 - name: Ensure users exist user: name: "{{ item.name }}" groups: "{{ item.groups }}" shell: "{{ item.shell }}" append: true loop: "{{ users }}" - name: Authorize SSH keys for cbalders authorized_key: user: cbalders key: "{{ item }}" state: present loop: "{{ ssh_authorized_keys }}" - name: Passwordless sudo for cbalders copy: content: "cbalders ALL=(ALL) NOPASSWD:ALL\n" dest: /etc/sudoers.d/90-cbalders owner: root group: root mode: '0440' # ---------- Node 22 (NodeSource apt repo) ---------- - name: Ensure /etc/apt/keyrings exists file: path: /etc/apt/keyrings state: directory mode: '0755' - name: Add NodeSource GPG key get_url: url: https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key dest: /etc/apt/keyrings/nodesource.asc mode: '0644' - name: Add NodeSource apt repo copy: content: "deb [signed-by=/etc/apt/keyrings/nodesource.asc] https://deb.nodesource.com/node_{{ meridian_node_major }}.x nodistro main\n" dest: /etc/apt/sources.list.d/nodesource.list owner: root group: root mode: '0644' register: nodesource_repo - name: apt update after NodeSource add apt: update_cache: true when: nodesource_repo.changed - name: Install Node.js apt: name: nodejs state: present - name: Verify Node major version command: node --version register: node_version changed_when: false - name: Fail if Node major mismatch fail: msg: "Node {{ node_version.stdout }} installed; expected v{{ meridian_node_major }}.x" when: not node_version.stdout.startswith("v" ~ meridian_node_major | string ~ ".") # ---------- Meridian user + home ---------- - name: Ensure meridian system user user: name: "{{ meridian_user }}" system: true home: "{{ meridian_home }}" shell: /usr/sbin/nologin create_home: true state: present - name: Ensure meridian home perms file: path: "{{ meridian_home }}" state: directory owner: "{{ meridian_user }}" group: "{{ meridian_user }}" mode: '0750' - name: Ensure .claude credentials dir file: path: "{{ meridian_home }}/.claude" state: directory owner: "{{ meridian_user }}" group: "{{ meridian_user }}" mode: '0700' # ---------- Install Meridian via npm ---------- - name: Install @rynfar/meridian globally npm: name: "@rynfar/meridian" global: true state: latest register: meridian_install - name: Resolve meridian binary path command: which meridian register: meridian_bin changed_when: false # ---------- systemd unit ---------- - name: Deploy meridian systemd unit template: src: meridian.service.j2 dest: /etc/systemd/system/meridian.service owner: root group: root mode: '0644' notify: reload systemd - name: Flush handlers (reload systemd before enable) meta: flush_handlers - name: Enable meridian service systemd: name: meridian enabled: true daemon_reload: true # OAuth creds (~/.claude/) are scp'd in manually after first deploy. # Start the service only if creds are present — otherwise the SDK would # crash-loop on missing credentials. - name: Check for Claude OAuth credentials stat: path: "{{ meridian_home }}/.claude/.credentials.json" register: claude_creds - name: Start meridian if credentials present systemd: name: meridian state: started when: claude_creds.stat.exists - name: Bootstrap reminder debug: msg: | Meridian installed at {{ meridian_bin.stdout }}. OAuth credentials not yet present at {{ meridian_home }}/.claude/.credentials.json. Bootstrap on your Mac: npm i -g @anthropic-ai/claude-code && claude login Then transfer creds: scp -r ~/.claude cbalders@{{ inventory_hostname }}:/tmp/.claude-bootstrap On the LXC: sudo cp -r /tmp/.claude-bootstrap/. {{ meridian_home }}/.claude/ sudo chown -R {{ meridian_user }}:{{ meridian_user }} {{ meridian_home }}/.claude/ sudo systemctl start meridian when: not claude_creds.stat.exists