A simple command-line tool for managing Outline VPN access keys via the Outline Server API.
# Clone the repository
git clone https://github.com/huangsen365/outline-cli.git
cd outline-cli
# Set up virtual environment
python -m venv venv
source venv/bin/activate
# Install dependencies
pip install outline-vpn-api python-dotenv
# Optional: only needed for the access-key test tools (ss_test.py / ss_proxy.py)
pip install cryptographyOn first run, the CLI will prompt for your Outline server credentials:
- API URL: Found in your Outline Manager (e.g.,
https://x.x.x.x:port/prefix) - Certificate SHA256: The certificate fingerprint from your Outline Manager
Credentials are saved to .env file for future use.
# List all access keys
python outline_cli.py list
# Show full details of a specific key
python outline_cli.py show <key_id>
# Generate a Clash config for a key
python outline_cli.py clash <key_id>
python outline_cli.py clash <key_id> --global # proxy everything
python outline_cli.py clash <key_id> --autoname # -> clash-<key name>.yaml
python outline_cli.py clash <key_id> --global --autoname # -> clash-<key name>-global.yaml
python outline_cli.py clash <key_id> --output clash-my-device.yaml
python outline_cli.py clash <key_id> --name "My Proxy"
# Create a new access key
python outline_cli.py add
python outline_cli.py add --name "My Device"
# Delete an access key
python outline_cli.py delete <key_id>
# Rename an access key
python outline_cli.py rename <key_id> "New Name"
# Set data limit (in MB, use 0 to remove limit)
python outline_cli.py limit <key_id> 1024
python outline_cli.py limit <key_id> 0$ python outline_cli.py list
ID Name Usage (MB) Access URL
--------------------------------------------------------------------------------
1 Device-A 125.3 ss://Y2hhY...@1.2.3.4:12345/?outline=1
2 Device-B 0.0 ss://Y2hhY...@1.2.3.4:12345/?outline=1
Two helper scripts verify that an ss:// key actually works. Both take the
access URL as an argument, support the chacha20-ietf-poly1305 cipher used by
Outline, and require the cryptography package (pip install cryptography).
# One-shot connectivity check: tunnels a single HTTP request through the key
# and prints each protocol step (handshake, encryption, response).
python ss_test.py 'ss://<base64>@host:port/?outline=1'
# Expose the key as a local SOCKS5 proxy so any client can route through it.
python ss_proxy.py 'ss://<base64>@host:port/?outline=1' --listen 127.0.0.1:1080
curl --socks5-hostname 127.0.0.1:1080 https://api.ipify.orgThe quickest way to get a Clash config is the clash command, which reads the
key and fills everything in for you:
# Print to stdout
python outline_cli.py --profile <profile> clash <key_id>
# Write to an auto-named file: clash-<proxy name>.yaml
python outline_cli.py --profile <profile> clash <key_id> --autoname
# Write to a filename you choose
python outline_cli.py --profile <profile> clash <key_id> -o clash-my-device.yamlThe proxy name defaults to the key's name; override it with --name. Because
--autoname always produces a clash-*.yaml file, the generated config is
already covered by .gitignore. To build a config by hand instead, read on.
Two rule sets are available, and two matching templates are included:
| Routing | Template | CLI flag | Behaviour |
|---|---|---|---|
| Split (default) | clash-template.yaml |
(none) | Mainland-China traffic and LAN stay direct; everything else is proxied |
| Global | clash-template-global.yaml |
--global / -g |
Everything except the LAN goes through the proxy |
--global works with --autoname and appends -global to the filename, so both
configs for one key can sit side by side:
python outline_cli.py --profile <profile> clash <key_id> --autoname
# -> clash-my-key.yaml (split)
python outline_cli.py --profile <profile> clash <key_id> --global --autoname
# -> clash-my-key-global.yaml (global)Load whichever one you need in the client, or keep both as separate profiles and
switch between them. Most Clash clients also have a Rule/Global/Direct toggle
(mode: global in the config) that overrides the rules at runtime.
To build a config by hand, copy the template you want, replace the <...>
placeholders with values from your access key, and load it in your Clash client.
Get a key's ss:// access URL:
python outline_cli.py --profile <profile> show <key_id>The URL has the form ss://<base64>@<server>:<port>/?outline=1. Decode the
<base64> part to reveal <cipher>:<password>:
echo '<base64>' | base64 -d
# -> chacha20-ietf-poly1305:<password>A filled-in example:
proxies:
- name: "clash-mi-on-mba-m4-jp"
type: ss
server: example.wansio.com
port: 2132
cipher: chacha20-ietf-poly1305
password: "your-password-here"
udp: true
proxy-groups:
- name: "Proxy"
type: select
proxies:
- "clash-mi-on-mba-m4-jp"
- DIRECT
rules:
- GEOIP,LAN,DIRECT
- GEOSITE,CN,DIRECT
- GEOIP,CN,DIRECT
- MATCH,ProxyGEOIP,CN only matches after DNS resolution, so CN sites served from foreign IPs
(CDNs) would still go through the proxy. GEOSITE,CN matches the domain itself,
and sits above GEOIP,CN so the domain match wins.
GEOSITE requires a Clash Meta / mihomo core — Clash Verge Rev, ClashX Meta,
Mihomo Party, FlClash, ClashMetaForAndroid and friends all qualify. On the
original Clash core, delete that line: it rejects the rule type and the config
will not load.
The global variant keeps the same proxies and proxy-groups; only the rules
change:
rules:
- GEOIP,LAN,DIRECT
- MATCH,ProxyKeep GEOIP,LAN,DIRECT even here — tunnelling 192.168.x.x would break your
router admin page, printers and mDNS. And note the rule target is
case-sensitive: MATCH,Proxy must match the proxy-group name exactly.
Note: a filled config contains your key's password. Keep it out of version control — the
.gitignoreignoresclash-*.yaml(except the template itself).
MIT