diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3cf35c3..e886b45 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: # node --check only looks at its first argument, so check each file in turn. - name: Check syntax run: | - for file in index.js shadowboxKey.js lib/*.js test/*.js; do + for file in index.js shadowtools.js lib/*.js test/*.js; do node --check "$file" done @@ -40,7 +40,7 @@ jobs: run: npm test - name: Check the CLI starts and prints help - run: node shadowboxKey.js --help + run: node shadowtools.js --help - name: Check the module exports load run: | diff --git a/README.md b/README.md index 52dc621..7c54197 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ShadowboxKeys +# shadowtools -[![CI](https://github.com/rahmanow/ShadowboxKeys/actions/workflows/ci.yml/badge.svg)](https://github.com/rahmanow/ShadowboxKeys/actions/workflows/ci.yml) +[![CI](https://github.com/rahmanow/shadowtools/actions/workflows/ci.yml/badge.svg)](https://github.com/rahmanow/shadowtools/actions/workflows/ci.yml) Manage access keys on your [Outline VPN](https://getoutline.org/) (Shadowbox) server, from the terminal, a local web interface, or your own code. It lists, creates, renames and deletes keys, sets per-key data limits, reports how much data each key has used, and prints scannable QR codes so people can onboard with the Outline app instead of copy-pasting `ss://` strings. @@ -17,14 +17,14 @@ This project supersedes [outline-br](https://github.com/rahmanow/outline-br), wh ## Installation ```bash -git clone https://github.com/rahmanow/ShadowboxKeys.git -cd ShadowboxKeys +git clone https://github.com/rahmanow/shadowtools.git +cd shadowtools npm install ``` ## Configuration -Configure the tool either with environment variables (recommended — keeps secrets out of the code) or by editing the constants at the top of `shadowboxKey.js`. +Configure the tool either with environment variables (recommended — keeps secrets out of the code) or by editing the constants at the top of `shadowtools.js`. | Setting | Environment variable | Description | | --- | --- | --- | @@ -56,7 +56,7 @@ source .env ## Commands ``` -node shadowboxKey.js [options] +node shadowtools.js [options] ``` | Command | What it does | @@ -89,7 +89,7 @@ Sizes accept a unit suffix: `10GB`, `500MB`, `2TB`, or a plain byte count. List every key: ```console -$ node shadowboxKey.js list +$ node shadowtools.js list ID NAME LIMIT ACCESS URL -- ----- ----- ----------------------------------------------------- 0 Alice 10 GB ss://YWVzOnBhc3N3b3Jk@vpn.example.com:443/?outline=1 @@ -99,7 +99,7 @@ ID NAME LIMIT ACCESS URL Create a key with a 50 GB cap and show its QR code: ```console -$ node shadowboxKey.js add Carol --limit 50GB --qr +$ node shadowtools.js add Carol --limit 50GB --qr Created key "Carol" (id 2) Data limit: 50 GB ss://bmV3a2V5@vpn.example.com:502/?outline=1 @@ -113,7 +113,7 @@ Carol: See who is using how much: ```console -$ node shadowboxKey.js usage +$ node shadowtools.js usage ID NAME USED LIMIT OF LIMIT -- ----- ------ ----- -------- 0 Alice 3.0 GB 10 GB 30% @@ -125,14 +125,14 @@ Total transferred: 3.5 GB Cap a heavy user, then lift the cap later: ```bash -node shadowboxKey.js limit Alice 10GB -node shadowboxKey.js limit Alice none +node shadowtools.js limit Alice 10GB +node shadowtools.js limit Alice none ``` Export usage for a spreadsheet: ```bash -node shadowboxKey.js usage --csv > usage.csv +node shadowtools.js usage --csv > usage.csv ``` ## Web interface @@ -140,7 +140,7 @@ node shadowboxKey.js usage --csv > usage.csv If you would rather click than type: ```bash -node shadowboxKey.js ui +node shadowtools.js ui ``` It prints a URL to open: @@ -186,7 +186,7 @@ port. Everything the CLI does is available as a module. `require` the package and you get three helpers plus the underlying client: ```js -const { listKeys, getUsage, getKeys, OutlineClient } = require('shadowbox-keys'); +const { listKeys, getUsage, getKeys, OutlineClient } = require('shadowtools'); const API = 'https://1.2.3.4:16942/AbCdEf123'; const options = { domain: 'vpn.example.com', certSha256: 'E3823F9B...52F5A584' }; @@ -221,7 +221,7 @@ await client.clearServerDataLimit(); Failures caused by bad input or a bad response throw `UserError`, which is also exported, so you can tell them apart from bugs: ```js -const { UserError } = require('shadowbox-keys'); +const { UserError } = require('shadowtools'); try { await listKeys(API, { certSha256: expected }); @@ -241,7 +241,7 @@ const keys = require('outline-br'); keys('https://1.2.3.4:16942/AbCdEf123', '87.65.43.21'); // after -const { getKeys } = require('shadowbox-keys'); +const { getKeys } = require('shadowtools'); await getKeys('https://1.2.3.4:16942/AbCdEf123', '87.65.43.21'); ``` @@ -253,7 +253,7 @@ New code should prefer `listKeys()`, which returns structured data and leaves pr ``` index.js Programmatic API: listKeys, getUsage, getKeys -shadowboxKey.js CLI entry point: argument parsing and commands +shadowtools.js CLI entry point: argument parsing and commands lib/outline.js Outline Management API client lib/format.js Byte formatting, size parsing, table/CSV output lib/errors.js UserError, for messages shown without a stack trace @@ -281,7 +281,7 @@ Outline servers use a self-signed TLS certificate for the Management API, so the Setting `OUTLINE_CERT_SHA256` closes that gap. The tool then checks the certificate the server presents against the fingerprint you configured, and **aborts before sending anything** if they differ: ```console -$ OUTLINE_CERT_SHA256=aaaa...aaaa node shadowboxKey.js list +$ OUTLINE_CERT_SHA256=aaaa...aaaa node shadowtools.js list The server presented an unexpected TLS certificate, so the request was not sent. expected: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa received: e3823f9bb490d35487ee013ec7d23a3662f76b95eb01a40f4851905152f5a584 @@ -303,7 +303,7 @@ The HTTP calls use the built-in `node:https` module because the global `fetch()` ## Troubleshooting -- **`Please configure your Management API URL first`** — set `OUTLINE_API_URL`, or replace the placeholder in `shadowboxKey.js`. +- **`Please configure your Management API URL first`** — set `OUTLINE_API_URL`, or replace the placeholder in `shadowtools.js`. - **`Could not reach the Outline server`** — check that the Management API URL is correct and that its port (usually `16942`) is reachable from your machine. - **`Cannot find module 'qrcode-terminal'`** — run `npm install` in the project directory first. - **`The server presented an unexpected TLS certificate`** — either `OUTLINE_CERT_SHA256` is stale (recopy `certSha256` from Outline Manager after rebuilding or migrating the server), or something other than your Outline server answered. See [certificate pinning](#certificate-pinning). diff --git a/index.js b/index.js index 1d704b8..d34ba94 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ /** * Programmatic entry point. * - * The CLI in shadowboxKey.js is one consumer of this API; the exports below are + * The CLI in shadowtools.js is one consumer of this API; the exports below are * the same building blocks it uses, so anything the CLI can do is reachable from * code. getKeys() is kept signature-compatible with the outline-br module this * project absorbed, so existing callers keep working. diff --git a/package.json b/package.json index 8c8d41e..5ed3d0f 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,19 @@ { - "name": "shadowbox-keys", - "version": "3.0.0", + "name": "shadowtools", + "version": "4.0.0", "description": "Manage Outline VPN (Shadowbox) access keys from the command line or from code: list, create, rename, delete, set data limits, report usage and print QR codes", "license": "MIT", "main": "index.js", "bin": { - "shadowbox-keys": "shadowboxKey.js" + "shadowtools": "shadowtools.js" }, "files": [ "index.js", - "shadowboxKey.js", + "shadowtools.js", "lib/" ], "scripts": { - "start": "node shadowboxKey.js", + "start": "node shadowtools.js", "test": "node --test" }, "keywords": [ @@ -28,7 +28,7 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/rahmanow/ShadowboxKeys.git" + "url": "git+https://github.com/rahmanow/shadowtools.git" }, "engines": { "node": ">=14" diff --git a/shadowboxKey.js b/shadowtools.js similarity index 96% rename from shadowboxKey.js rename to shadowtools.js index 17598a7..0abeb31 100755 --- a/shadowboxKey.js +++ b/shadowtools.js @@ -14,9 +14,9 @@ const { OutlineClient } = require('./lib/outline'); const { UserError } = require('./lib/errors'); const { formatBytes, parseBytes, rewriteAccessUrl, printTable, printCsv } = require('./lib/format'); -const USAGE = `ShadowboxKeys - manage access keys on your Outline VPN server +const USAGE = `shadowtools - manage access keys on your Outline VPN server -Usage: node shadowboxKey.js [options] +Usage: node shadowtools.js [options] Commands: list List every access key with its access URL @@ -49,11 +49,11 @@ Configuration (environment variables): any server presenting a different certificate. Examples: - node shadowboxKey.js list --qr - node shadowboxKey.js add Alice --limit 50GB - node shadowboxKey.js limit Alice 10GB - node shadowboxKey.js usage --csv - node shadowboxKey.js ui --port 9000 + node shadowtools.js list --qr + node shadowtools.js add Alice --limit 50GB + node shadowtools.js limit Alice 10GB + node shadowtools.js usage --csv + node shadowtools.js ui --port 9000 `; /** Splits argv into positional arguments and a flag map. */ @@ -285,7 +285,7 @@ async function main() { return; } - // Listing is the historical default, so bare `node shadowboxKey.js` still works. + // Listing is the historical default, so bare `node shadowtools.js` still works. const command = commands[commandName || 'list']; if (!command) { throw new UserError(`Unknown command "${commandName}". Run with --help to see what is available.`); @@ -294,7 +294,7 @@ async function main() { if (managementApiUrl.includes('xx.xx.xx.xxx')) { throw new UserError( 'Please configure your Management API URL first.\n' + - 'Set the OUTLINE_API_URL environment variable, or edit the managementApiUrl constant in shadowboxKey.js.' + 'Set the OUTLINE_API_URL environment variable, or edit the managementApiUrl constant in shadowtools.js.' ); } diff --git a/test/api.test.js b/test/api.test.js index 38f7706..9228ef6 100644 --- a/test/api.test.js +++ b/test/api.test.js @@ -23,7 +23,7 @@ const hasOpenssl = (() => { /** Creates a throwaway self-signed certificate, mimicking an Outline server's. */ function createCertificate() { - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'shadowbox-keys-test-')); + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'shadowtools-test-')); const keyPath = path.join(dir, 'key.pem'); const certPath = path.join(dir, 'cert.pem'); diff --git a/test/cli.test.js b/test/cli.test.js index 231d035..52b393d 100644 --- a/test/cli.test.js +++ b/test/cli.test.js @@ -3,7 +3,7 @@ const test = require('node:test'); const assert = require('node:assert'); -const { parseArgs, findKey } = require('../shadowboxKey'); +const { parseArgs, findKey } = require('../shadowtools'); const { OutlineClient, normalizeFingerprint } = require('../lib/outline'); const { UserError } = require('../lib/errors');