relic 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -0
- package/bin/relic +38 -8
- package/package.json +8 -5
- package/postinstall.mjs +0 -102
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Relic
|
|
2
|
+
|
|
3
|
+
Zero-knowledge secret layer for your projects. Encrypted on your device, never exposed to anyone else. Not even us.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm
|
|
9
|
+
npm install -g relic
|
|
10
|
+
|
|
11
|
+
# Homebrew
|
|
12
|
+
brew install heycupola/tap/relic
|
|
13
|
+
|
|
14
|
+
# Download binary
|
|
15
|
+
curl -fsSL https://github.com/heycupola/relic/releases/latest/download/relic-$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m).tar.gz | tar -xz -C /usr/local/bin
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Launch the TUI
|
|
22
|
+
relic
|
|
23
|
+
|
|
24
|
+
# Authenticate
|
|
25
|
+
relic login
|
|
26
|
+
|
|
27
|
+
# Initialize a project
|
|
28
|
+
relic init
|
|
29
|
+
|
|
30
|
+
# Run a command with secrets injected
|
|
31
|
+
relic run -e production -- npm run deploy
|
|
32
|
+
relic run -e staging -f database -- ./migrate.sh
|
|
33
|
+
relic run -e production -s client -- npm run build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Commands
|
|
37
|
+
|
|
38
|
+
| Command | Description |
|
|
39
|
+
|---------|-------------|
|
|
40
|
+
| `relic` | Launch the TUI (default) |
|
|
41
|
+
| `relic login` | Authenticate via device code flow |
|
|
42
|
+
| `relic logout` | Clear session and cached data |
|
|
43
|
+
| `relic whoami` | Show current user |
|
|
44
|
+
| `relic projects` | List projects with environments and folders |
|
|
45
|
+
| `relic init` | Create `relic.toml` for the current project |
|
|
46
|
+
| `relic run` | Run a command with secrets injected |
|
|
47
|
+
| `relic telemetry` | Manage anonymous usage data collection |
|
|
48
|
+
|
|
49
|
+
### `relic run` options
|
|
50
|
+
|
|
51
|
+
| Flag | Description |
|
|
52
|
+
|------|-------------|
|
|
53
|
+
| `-e, --environment` | Environment name (required) |
|
|
54
|
+
| `-f, --folder` | Folder name |
|
|
55
|
+
| `-s, --scope` | `client`, `server`, or `shared` |
|
|
56
|
+
| `-p, --project` | Project ID (overrides `relic.toml`) |
|
|
57
|
+
|
|
58
|
+
## CI/CD
|
|
59
|
+
|
|
60
|
+
Use API keys for non-interactive environments:
|
|
61
|
+
|
|
62
|
+
```yaml
|
|
63
|
+
# GitHub Actions
|
|
64
|
+
- name: Deploy with secrets
|
|
65
|
+
env:
|
|
66
|
+
RELIC_API_KEY: ${{ secrets.RELIC_API_KEY }}
|
|
67
|
+
RELIC_PASSWORD: ${{ secrets.RELIC_PASSWORD }}
|
|
68
|
+
run: npx relic run -e production -- npm run deploy
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
| Variable | Description |
|
|
72
|
+
|----------|-------------|
|
|
73
|
+
| `RELIC_API_KEY` | API key for authentication |
|
|
74
|
+
| `RELIC_PASSWORD` | Master password for decryption |
|
|
75
|
+
| `RELIC_PROJECT_ID` | Project ID (optional if `relic.toml` exists) |
|
|
76
|
+
|
|
77
|
+
## Supported Platforms
|
|
78
|
+
|
|
79
|
+
| Platform | Architecture |
|
|
80
|
+
|----------|-------------|
|
|
81
|
+
| macOS | ARM64 (Apple Silicon) |
|
|
82
|
+
| macOS | x64 (Intel) |
|
|
83
|
+
| Linux | x64 |
|
|
84
|
+
| Windows | x64 |
|
|
85
|
+
|
|
86
|
+
## Links
|
|
87
|
+
|
|
88
|
+
- [Website](https://heyrelic.com)
|
|
89
|
+
- [Documentation](https://docs.heyrelic.com)
|
|
90
|
+
- [GitHub](https://github.com/heycupola/relic)
|
package/bin/relic
CHANGED
|
@@ -3,20 +3,50 @@
|
|
|
3
3
|
import { execFileSync } from "node:child_process";
|
|
4
4
|
import { existsSync } from "node:fs";
|
|
5
5
|
import { join, dirname } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
import { platform } from "node:os";
|
|
6
|
+
import { createRequire } from "node:module";
|
|
7
|
+
import { platform, arch } from "node:os";
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const PLATFORM_MAP = {
|
|
10
|
+
"darwin-arm64": "relic-cli-darwin-arm64",
|
|
11
|
+
"darwin-x64": "relic-cli-darwin-x64",
|
|
12
|
+
"linux-x64": "relic-cli-linux-x64",
|
|
13
|
+
"win32-x64": "relic-cli-windows-x64",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const currentPlatform = `${platform()}-${arch()}`;
|
|
17
|
+
const pkg = PLATFORM_MAP[currentPlatform];
|
|
18
|
+
|
|
19
|
+
if (!pkg) {
|
|
20
|
+
console.error(
|
|
21
|
+
`relic: Unsupported platform ${currentPlatform}.\n` +
|
|
22
|
+
`Supported: ${Object.keys(PLATFORM_MAP).join(", ")}\n` +
|
|
23
|
+
`Download manually from: https://github.com/heycupola/relic/releases`
|
|
24
|
+
);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const require = createRequire(import.meta.url);
|
|
10
29
|
const isWindows = platform() === "win32";
|
|
11
30
|
const binaryName = isWindows ? "relic.exe" : "relic";
|
|
12
|
-
|
|
31
|
+
|
|
32
|
+
let binary;
|
|
33
|
+
try {
|
|
34
|
+
const pkgJson = require.resolve(`${pkg}/package.json`);
|
|
35
|
+
binary = join(dirname(pkgJson), "bin", binaryName);
|
|
36
|
+
} catch {
|
|
37
|
+
console.error(
|
|
38
|
+
`relic: Platform package ${pkg} is not installed.\n` +
|
|
39
|
+
`Try reinstalling: npm install -g relic\n\n` +
|
|
40
|
+
`If the problem persists, download from:\n` +
|
|
41
|
+
` https://github.com/heycupola/relic/releases`
|
|
42
|
+
);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
13
45
|
|
|
14
46
|
if (!existsSync(binary)) {
|
|
15
47
|
console.error(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"Or download manually from:\n" +
|
|
19
|
-
" https://github.com/heycupola/relic/releases"
|
|
48
|
+
`relic: Binary not found at ${binary}\n` +
|
|
49
|
+
`Try reinstalling: npm install -g relic`
|
|
20
50
|
);
|
|
21
51
|
process.exit(1);
|
|
22
52
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "The Relic CLI for managing and sharing secrets. Encrypted on your device, never exposed to anyone else. Not even us.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,13 +18,16 @@
|
|
|
18
18
|
"bin": {
|
|
19
19
|
"relic": "bin/relic"
|
|
20
20
|
},
|
|
21
|
-
"scripts": {
|
|
22
|
-
"postinstall": "node postinstall.mjs"
|
|
23
|
-
},
|
|
24
21
|
"files": [
|
|
25
22
|
"bin/",
|
|
26
|
-
"
|
|
23
|
+
"README.md"
|
|
27
24
|
],
|
|
25
|
+
"optionalDependencies": {
|
|
26
|
+
"relic-cli-darwin-arm64": "0.6.1",
|
|
27
|
+
"relic-cli-darwin-x64": "0.6.1",
|
|
28
|
+
"relic-cli-linux-x64": "0.6.1",
|
|
29
|
+
"relic-cli-windows-x64": "0.6.1"
|
|
30
|
+
},
|
|
28
31
|
"engines": {
|
|
29
32
|
"node": ">=18"
|
|
30
33
|
}
|
package/postinstall.mjs
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { execSync } from "node:child_process";
|
|
2
|
-
import { createWriteStream, existsSync, mkdirSync, chmodSync, renameSync, unlinkSync } from "node:fs";
|
|
3
|
-
import { join, dirname } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { platform, arch } from "node:os";
|
|
6
|
-
import { get } from "node:https";
|
|
7
|
-
import { createGunzip } from "node:zlib";
|
|
8
|
-
import { readFileSync } from "node:fs";
|
|
9
|
-
|
|
10
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
-
const pkg = JSON.parse(readFileSync(join(__dirname, "package.json"), "utf8"));
|
|
12
|
-
const version = pkg.version;
|
|
13
|
-
|
|
14
|
-
const PLATFORM_MAP = {
|
|
15
|
-
"darwin-arm64": { archive: `relic-darwin-arm64.tar.gz`, binary: "relic" },
|
|
16
|
-
"darwin-x64": { archive: `relic-darwin-x64.tar.gz`, binary: "relic" },
|
|
17
|
-
"linux-x64": { archive: `relic-linux-x64.tar.gz`, binary: "relic" },
|
|
18
|
-
"win32-x64": { archive: `relic-win32-x64.zip`, binary: "relic.exe" },
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const currentPlatform = `${platform()}-${arch()}`;
|
|
22
|
-
const target = PLATFORM_MAP[currentPlatform];
|
|
23
|
-
|
|
24
|
-
if (!target) {
|
|
25
|
-
console.warn(
|
|
26
|
-
`[relic] Unsupported platform: ${currentPlatform}. ` +
|
|
27
|
-
`Supported: ${Object.keys(PLATFORM_MAP).join(", ")}`
|
|
28
|
-
);
|
|
29
|
-
process.exit(0);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const binDir = join(__dirname, "bin");
|
|
33
|
-
const binaryPath = join(binDir, target.binary);
|
|
34
|
-
|
|
35
|
-
if (existsSync(binaryPath)) {
|
|
36
|
-
process.exit(0);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const url = `https://github.com/heycupola/relic/releases/download/v${version}/${target.archive}`;
|
|
40
|
-
|
|
41
|
-
function download(url) {
|
|
42
|
-
return new Promise((resolve, reject) => {
|
|
43
|
-
get(url, (res) => {
|
|
44
|
-
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
45
|
-
return download(res.headers.location).then(resolve, reject);
|
|
46
|
-
}
|
|
47
|
-
if (res.statusCode !== 200) {
|
|
48
|
-
return reject(new Error(`Download failed: HTTP ${res.statusCode} for ${url}`));
|
|
49
|
-
}
|
|
50
|
-
resolve(res);
|
|
51
|
-
}).on("error", reject);
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function extractTarGz(stream, destDir) {
|
|
56
|
-
const tmpFile = join(destDir, "_tmp.tar.gz");
|
|
57
|
-
await new Promise((resolve, reject) => {
|
|
58
|
-
const ws = createWriteStream(tmpFile);
|
|
59
|
-
stream.pipe(ws);
|
|
60
|
-
ws.on("finish", resolve);
|
|
61
|
-
ws.on("error", reject);
|
|
62
|
-
});
|
|
63
|
-
execSync(`tar -xzf "${tmpFile}" -C "${destDir}"`, { stdio: "ignore" });
|
|
64
|
-
unlinkSync(tmpFile);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function extractZip(stream, destDir) {
|
|
68
|
-
const tmpFile = join(destDir, "_tmp.zip");
|
|
69
|
-
await new Promise((resolve, reject) => {
|
|
70
|
-
const ws = createWriteStream(tmpFile);
|
|
71
|
-
stream.pipe(ws);
|
|
72
|
-
ws.on("finish", resolve);
|
|
73
|
-
ws.on("error", reject);
|
|
74
|
-
});
|
|
75
|
-
execSync(`unzip -o -q "${tmpFile}" -d "${destDir}"`, { stdio: "ignore" });
|
|
76
|
-
unlinkSync(tmpFile);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
mkdirSync(binDir, { recursive: true });
|
|
81
|
-
|
|
82
|
-
console.log(`[relic] Downloading ${target.archive} for ${currentPlatform}...`);
|
|
83
|
-
const stream = await download(url);
|
|
84
|
-
|
|
85
|
-
if (target.archive.endsWith(".tar.gz")) {
|
|
86
|
-
await extractTarGz(stream, binDir);
|
|
87
|
-
} else {
|
|
88
|
-
await extractZip(stream, binDir);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (existsSync(binaryPath)) {
|
|
92
|
-
chmodSync(binaryPath, 0o755);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
console.log(`[relic] Installed successfully.`);
|
|
96
|
-
} catch (err) {
|
|
97
|
-
console.warn(
|
|
98
|
-
`[relic] Failed to download binary: ${err.message}\n` +
|
|
99
|
-
`[relic] You can download manually from: https://github.com/heycupola/relic/releases`
|
|
100
|
-
);
|
|
101
|
-
process.exit(0);
|
|
102
|
-
}
|