## ADDED Requirements ### Requirement: shadowsocks-rust is installed on the relay server The shadowsocks role SHALL download and install the shadowsocks-rust `ssserver` binary from GitHub releases to a configurable path, with a configurable version. #### Scenario: Fresh installation - **WHEN** the shadowsocks role runs on a relay server without shadowsocks-rust installed - **THEN** the specified version of `ssserver` binary is downloaded and installed - **THEN** the binary is executable and owned by a dedicated service user #### Scenario: Version upgrade - **WHEN** the shadowsocks version variable is changed and the playbook is re-run - **THEN** the binary is replaced with the new version - **THEN** the service is restarted ### Requirement: shadowsocks-rust runs as a systemd service The shadowsocks role SHALL create a systemd unit file for `ssserver` and ensure it is enabled and started. #### Scenario: Service is running - **WHEN** the shadowsocks role completes - **THEN** the `ssserver` systemd service is enabled and running - **THEN** the service runs under a dedicated non-root user ### Requirement: shadowsocks-rust configuration is templated The relay server's shadowsocks configuration SHALL be generated from a Jinja2 template with variables for port, password, and cipher method. #### Scenario: Configuration is generated from template - **WHEN** the shadowsocks role runs - **THEN** a JSON config file is rendered from template with port, password, and encryption method - **THEN** the config file permissions are restricted (readable only by the service user) #### Scenario: Configuration change triggers restart - **WHEN** a variable in `group_vars/relay.yml` is changed and the playbook is re-run - **THEN** the configuration file is updated - **THEN** the ssserver service is restarted ### Requirement: shadowsocks uses AEAD cipher The shadowsocks configuration SHALL use an AEAD cipher (e.g., `aes-256-gcm` or `chacha20-ietf-poly1305`), configurable via Ansible variable. #### Scenario: AEAD cipher is configured - **WHEN** the shadowsocks configuration is generated - **THEN** the `method` field uses the configured AEAD cipher - **THEN** the default cipher is `aes-256-gcm` if not overridden ### Requirement: shadowsocks credentials are managed via Ansible Vault The relay server's shadowsocks password SHALL be defined in `group_vars` and encrypted with Ansible Vault. #### Scenario: Password is not in plaintext in repository - **WHEN** the shadowsocks role generates the configuration - **THEN** the password comes from an Ansible Vault-encrypted variable - **THEN** no plaintext password exists in version-controlled files