relic 0.4.4 → 0.5.0
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/bin/relic +37 -0
- package/package.json +15 -53
- package/postinstall.mjs +102 -0
- package/README.md +0 -129
- package/dist/cli.js +0 -64633
- package/dist/highlights-eq9cgrbb.scm +0 -604
- package/dist/highlights-ghv9g403.scm +0 -205
- package/dist/highlights-hk7bwhj4.scm +0 -284
- package/dist/highlights-r812a2qc.scm +0 -150
- package/dist/highlights-x6tmsnaa.scm +0 -115
- package/dist/injections-73j83es3.scm +0 -27
- package/dist/tree-sitter-javascript-nd0q4pe9.wasm +0 -0
- package/dist/tree-sitter-markdown-411r6y9b.wasm +0 -0
- package/dist/tree-sitter-markdown_inline-j5349f42.wasm +0 -0
- package/dist/tree-sitter-typescript-zxjzwt75.wasm +0 -0
- package/dist/tree-sitter-zig-e78zbjpm.wasm +0 -0
- package/prebuilds/darwin-arm64/.gitkeep +0 -0
- package/prebuilds/darwin-arm64/librelic_runner.d +0 -1
- package/prebuilds/darwin-arm64/librelic_runner.dylib +0 -0
- package/prebuilds/darwin-x64/.gitkeep +0 -0
- package/prebuilds/darwin-x64/librelic_runner.d +0 -1
- package/prebuilds/darwin-x64/librelic_runner.dylib +0 -0
- package/prebuilds/linux-x64/.gitkeep +0 -0
- package/prebuilds/linux-x64/librelic_runner.d +0 -1
- package/prebuilds/linux-x64/librelic_runner.so +0 -0
- package/prebuilds/win32-x64/.gitkeep +0 -0
- package/prebuilds/win32-x64/relic_runner.dll +0 -0
package/bin/relic
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { existsSync } from "node:fs";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { platform } from "node:os";
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const isWindows = platform() === "win32";
|
|
11
|
+
const binaryName = isWindows ? "relic.exe" : "relic";
|
|
12
|
+
const binary = join(__dirname, binaryName);
|
|
13
|
+
|
|
14
|
+
if (!existsSync(binary)) {
|
|
15
|
+
console.error(
|
|
16
|
+
"relic: Binary not found. Run the postinstall script or reinstall:\n" +
|
|
17
|
+
" npm install -g relic\n\n" +
|
|
18
|
+
"Or download manually from:\n" +
|
|
19
|
+
" https://github.com/heycupola/relic/releases"
|
|
20
|
+
);
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
execFileSync(binary, process.argv.slice(2), {
|
|
26
|
+
stdio: "inherit",
|
|
27
|
+
env: process.env,
|
|
28
|
+
});
|
|
29
|
+
} catch (err) {
|
|
30
|
+
if (err.status !== null && err.status !== undefined) {
|
|
31
|
+
process.exit(err.status);
|
|
32
|
+
}
|
|
33
|
+
if (err.signal) {
|
|
34
|
+
process.kill(process.pid, err.signal);
|
|
35
|
+
}
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,69 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relic",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "The Relic CLI for managing and sharing secrets. Encrypted on your device, never exposed to anyone else. Not even us.",
|
|
5
|
-
"
|
|
6
|
-
|
|
7
|
-
"tui"
|
|
8
|
-
],
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
9
7
|
"repository": {
|
|
10
8
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/heycupola/relic.git"
|
|
12
|
-
"directory": "apps/cli"
|
|
9
|
+
"url": "https://github.com/heycupola/relic.git"
|
|
13
10
|
},
|
|
11
|
+
"keywords": [
|
|
12
|
+
"cli",
|
|
13
|
+
"secrets",
|
|
14
|
+
"encryption",
|
|
15
|
+
"security"
|
|
16
|
+
],
|
|
14
17
|
"author": "Can Vardar",
|
|
15
|
-
"license": "MIT",
|
|
16
|
-
"module": "index.ts",
|
|
17
|
-
"type": "module",
|
|
18
18
|
"bin": {
|
|
19
|
-
"relic": "
|
|
19
|
+
"relic": "bin/relic"
|
|
20
20
|
},
|
|
21
21
|
"scripts": {
|
|
22
|
-
"
|
|
23
|
-
"build:runner": "cd ../../packages/runner && cargo build --release",
|
|
24
|
-
"prepublishOnly": "bun run build",
|
|
25
|
-
"dev:cli": "DEV=true bun run build:runner && bun run index.ts",
|
|
26
|
-
"log:watch": "bun run ../../packages/logger/scripts/watch.ts",
|
|
27
|
-
"lint": "biome check --write .",
|
|
28
|
-
"format": "biome format --write .",
|
|
29
|
-
"check-types": "tsc --noEmit",
|
|
30
|
-
"test": "bun test"
|
|
22
|
+
"postinstall": "node postinstall.mjs"
|
|
31
23
|
},
|
|
32
24
|
"files": [
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"README.md"
|
|
25
|
+
"bin/",
|
|
26
|
+
"postinstall.mjs"
|
|
36
27
|
],
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"argon2": "^0.41.1"
|
|
39
|
-
},
|
|
40
|
-
"optionalDependencies": {
|
|
41
|
-
"@opentui/core-darwin-arm64": "0.1.65",
|
|
42
|
-
"@opentui/core-darwin-x64": "0.1.65",
|
|
43
|
-
"@opentui/core-linux-x64": "0.1.65",
|
|
44
|
-
"@opentui/core-win32-x64": "0.1.65"
|
|
45
|
-
},
|
|
46
|
-
"devDependencies": {
|
|
47
|
-
"@clack/prompts": "^0.11.0",
|
|
48
|
-
"@repo/auth": "*",
|
|
49
|
-
"@repo/backend": "*",
|
|
50
|
-
"@repo/crypto": "*",
|
|
51
|
-
"@repo/logger": "*",
|
|
52
|
-
"@repo/tui": "*",
|
|
53
|
-
"@repo/typescript-config": "*",
|
|
54
|
-
"@types/bun": "latest",
|
|
55
|
-
"bun-types": "^1.3.1",
|
|
56
|
-
"commander": "^13.1.0",
|
|
57
|
-
"convex": "catalog:",
|
|
58
|
-
"open": "^11.0.0",
|
|
59
|
-
"ora": "^8.2.0",
|
|
60
|
-
"picocolors": "^1.1.1",
|
|
61
|
-
"smol-toml": "^1.6.0"
|
|
62
|
-
},
|
|
63
|
-
"peerDependencies": {
|
|
64
|
-
"typescript": "^5"
|
|
65
|
-
},
|
|
66
28
|
"engines": {
|
|
67
29
|
"node": ">=18"
|
|
68
30
|
}
|
|
69
|
-
}
|
|
31
|
+
}
|
package/postinstall.mjs
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
}
|
package/README.md
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
# Relic CLI
|
|
2
|
-
|
|
3
|
-
Zero-knowledge secret layer CLI. Fetches encrypted secrets from the server, decrypts them locally, and injects them into the process environment.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun install
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Commands
|
|
12
|
-
|
|
13
|
-
| Command | Description |
|
|
14
|
-
|---------|-------------|
|
|
15
|
-
| `relic` | Launch the TUI (default) |
|
|
16
|
-
| `relic login` | Authenticate via device code flow |
|
|
17
|
-
| `relic logout` | Clear session, cached keys, and password |
|
|
18
|
-
| `relic whoami` | Show current user (name, email, plan) |
|
|
19
|
-
| `relic projects` | List projects with environments and folders |
|
|
20
|
-
| `relic init` | Create `relic.toml` and `.relic/` directory |
|
|
21
|
-
| `relic run` | Run a command with secrets injected |
|
|
22
|
-
| `relic telemetry status` | Show telemetry status |
|
|
23
|
-
| `relic telemetry enable` | Enable telemetry |
|
|
24
|
-
| `relic telemetry disable` | Disable telemetry |
|
|
25
|
-
|
|
26
|
-
### `relic run`
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
relic run -e <environment> [options] -- <command>
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
| Flag | Description |
|
|
33
|
-
|------|-------------|
|
|
34
|
-
| `-e, --environment` | Environment name (required) |
|
|
35
|
-
| `-f, --folder` | Folder name |
|
|
36
|
-
| `-s, --scope` | `client`, `server`, or `shared` |
|
|
37
|
-
| `-p, --project` | Project ID (overrides `relic.toml`) |
|
|
38
|
-
|
|
39
|
-
```bash
|
|
40
|
-
relic run -e production -- npm run deploy
|
|
41
|
-
relic run -e staging -f database -- ./migrate.sh
|
|
42
|
-
relic run -e production -s client -- npm run build
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
## Configuration
|
|
46
|
-
|
|
47
|
-
`relic.toml` in project root:
|
|
48
|
-
|
|
49
|
-
```toml
|
|
50
|
-
project_id = "<uuid>"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
Created by `relic init`. The CLI walks up from the current directory to find it.
|
|
54
|
-
|
|
55
|
-
## Runner (FFI)
|
|
56
|
-
|
|
57
|
-
Secret injection uses a Rust binary (`packages/runner`) loaded via Bun FFI (`dlopen`). The runner:
|
|
58
|
-
|
|
59
|
-
- Spawns the child process with a clean environment (`env_clear()`)
|
|
60
|
-
- Injects only the decrypted secrets
|
|
61
|
-
- Forwards signals (SIGTERM, SIGINT)
|
|
62
|
-
- Uses `Zeroizing` for secret memory and disables core dumps
|
|
63
|
-
|
|
64
|
-
Prebuilt binaries in `prebuilds/` for: `darwin-arm64`, `darwin-x64`, `linux-x64`, `win32-x64`.
|
|
65
|
-
|
|
66
|
-
## Caching
|
|
67
|
-
|
|
68
|
-
Local SQLite cache at `.relic/cache.db` (relative to `relic.toml` location). Used in session mode only; API key mode always fetches fresh data.
|
|
69
|
-
|
|
70
|
-
**Cached data:** environment/folder ID mappings, encrypted secrets, encrypted project key.
|
|
71
|
-
|
|
72
|
-
**Invalidation:** on each `relic run`, the CLI compares local `lastCachedAt` against the backend `updatedAt`. Stale cache triggers a fresh fetch. Key rotation invalidates all caches.
|
|
73
|
-
|
|
74
|
-
Scope filtering (`--scope`) is applied locally against cached data.
|
|
75
|
-
|
|
76
|
-
## CI/CD
|
|
77
|
-
|
|
78
|
-
Use API keys instead of interactive login:
|
|
79
|
-
|
|
80
|
-
| Variable | Description |
|
|
81
|
-
|----------|-------------|
|
|
82
|
-
| `RELIC_API_KEY` | API key for authentication |
|
|
83
|
-
| `RELIC_PASSWORD` | Master password for decryption |
|
|
84
|
-
| `RELIC_PROJECT_ID` | Project ID (optional if `relic.toml` exists) |
|
|
85
|
-
| `CONVEX_SITE_URL` | Convex HTTP actions URL |
|
|
86
|
-
|
|
87
|
-
### GitHub Actions
|
|
88
|
-
|
|
89
|
-
```yaml
|
|
90
|
-
- name: Deploy with secrets
|
|
91
|
-
env:
|
|
92
|
-
RELIC_API_KEY: ${{ secrets.RELIC_API_KEY }}
|
|
93
|
-
RELIC_PASSWORD: ${{ secrets.RELIC_PASSWORD }}
|
|
94
|
-
RELIC_PROJECT_ID: ${{ secrets.RELIC_PROJECT_ID }}
|
|
95
|
-
CONVEX_URL: ${{ secrets.CONVEX_URL }}
|
|
96
|
-
CONVEX_SITE_URL: ${{ secrets.CONVEX_SITE_URL }}
|
|
97
|
-
run: bunx relic run -e production -- npm run deploy
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Structure
|
|
101
|
-
|
|
102
|
-
```
|
|
103
|
-
├── index.ts # Entry point (commander setup)
|
|
104
|
-
├── commands/
|
|
105
|
-
│ ├── init.ts # relic init
|
|
106
|
-
│ ├── login.ts # relic login
|
|
107
|
-
│ ├── logout.ts # relic logout
|
|
108
|
-
│ ├── whoami.ts # relic whoami
|
|
109
|
-
│ ├── projects.ts # relic projects
|
|
110
|
-
│ ├── run.ts # relic run
|
|
111
|
-
│ └── telemetry.ts # relic telemetry
|
|
112
|
-
├── lib/
|
|
113
|
-
│ ├── api.ts # Convex API client, secret export
|
|
114
|
-
│ ├── config.ts # relic.toml loading/saving
|
|
115
|
-
│ ├── crypto.ts # Secret decryption helpers
|
|
116
|
-
│ └── types.ts # SecretScope type
|
|
117
|
-
├── ffi/
|
|
118
|
-
│ ├── bridge.ts # RunnerBridge FFI wrapper
|
|
119
|
-
│ ├── helper.ts # Library loading (dlopen)
|
|
120
|
-
│ └── constants.ts # URLs
|
|
121
|
-
└── helpers/
|
|
122
|
-
└── cache.ts # SQLite cache
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Development
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
|
-
bun run index.ts
|
|
129
|
-
```
|