## ADDED Requirements ### Requirement: SS password is auto-generated on first run The playbook SHALL generate a random 32-character alphanumeric password for Shadowsocks on first run, persisted to `credentials/ss_password` on the Ansible controller. #### Scenario: First run generates password - **WHEN** the playbook runs for the first time and `credentials/ss_password` does not exist - **THEN** a random 32-character password is generated and saved to `credentials/ss_password` - **THEN** the generated password is used for SS configuration #### Scenario: Subsequent runs reuse password - **WHEN** the playbook runs again and `credentials/ss_password` exists - **THEN** the existing password is read and reused ### Requirement: SS port is auto-generated on first run The playbook SHALL generate a random port in the 10000–49999 range for Shadowsocks on first run, persisted to `credentials/ss_port`. #### Scenario: First run generates port - **WHEN** the playbook runs for the first time and `credentials/ss_port` does not exist - **THEN** a random port number (10000–49999) is generated and saved - **THEN** the generated port is used for SS configuration and UFW rules ### Requirement: Trojan password is auto-generated on first run The playbook SHALL generate a random 32-character alphanumeric password for Trojan on first run, persisted to `credentials/trojan_password`. #### Scenario: First run generates password - **WHEN** the playbook runs for the first time and `credentials/trojan_password` does not exist - **THEN** a random 32-character password is generated and saved ### Requirement: Credentials directory is gitignored The `credentials/` directory SHALL be listed in `.gitignore` to prevent secrets from being committed. #### Scenario: Gitignore includes credentials - **WHEN** the repository is inspected - **THEN** `.gitignore` contains entries for `credentials/` and `output/` ### Requirement: Manual override is supported Users SHALL be able to override auto-generated values by setting variables via `--extra-vars` or in `group_vars`. #### Scenario: Manual override via extra-vars - **WHEN** the playbook is run with `--extra-vars "ss_password=my-custom-pass"` - **THEN** the custom value is used instead of the auto-generated one