openvoidnet 1.0.1 → 1.0.3

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 CHANGED
@@ -22,6 +22,10 @@ Headless and CI — paste a console-issued token:
22
22
  voidnet login --token vnp-pub-...
23
23
  ```
24
24
 
25
+ ## Security
26
+
27
+ Custom hosts (`--base`, `--auth-base`, `--live-base`, `VOIDNET_*_BASE`) receive bearer tokens, so using them requires explicit consent: interactive runs prompt once, pipes and CI need `--yes` or `VOIDNET_ASSUME_YES=1`, otherwise nothing credentialed is sent. Never run the CLI with host overrides you did not configure yourself.
28
+
25
29
  ## Commands
26
30
 
27
31
  ```bash
@@ -38,4 +42,4 @@ voidnet logout
38
42
  Browser login grants read + verify scopes only. Publishing and key rotation
39
43
  require a deliberately console-issued token.
40
44
 
41
- Proprietary Voidnet ecosystem. Not open source.
45
+ Licensed under the Apache License, Version 2.0.
package/bin/voidnet.js CHANGED
@@ -43,6 +43,7 @@ async function authed(path, opts = {}) {
43
43
  if (!cfg.token) {
44
44
  out.error(NOT_LOGGED_IN);
45
45
  }
46
+ await confirmCustomHosts();
46
47
  const url = `${ENV.mgmtBase}${path}`;
47
48
  out.diag(`request ${opts.method || 'GET'} ${url}`, { verboseOnly: true });
48
49
  const res = await fetch(url, {
@@ -61,10 +62,40 @@ async function authed(path, opts = {}) {
61
62
  const [cmd, sub, ...rest] = process.argv.slice(2);
62
63
  const tokenIdx = process.argv.indexOf('--token');
63
64
 
65
+ // Custom-host consent: any base deviating from the baked table receives
66
+ // bearer tokens, so sending credentials there requires explicit consent.
67
+ // --yes flag or VOIDNET_ASSUME_YES=1 pre-approves (CI sets it once and forgets).
68
+ // Interactive terminals get one prompt; pipes abort loud instead of hanging.
69
+ async function confirmCustomHosts() {
70
+ const o = ENV.overridden || { mgmt: false, auth: false, live: false };
71
+ if (!o.mgmt && !o.auth && !o.live) return;
72
+ const parts = [];
73
+ if (o.mgmt) parts.push(`mgmt=${ENV.mgmtBase}`);
74
+ if (o.auth) parts.push(`auth=${ENV.authBase}`);
75
+ if (o.live) parts.push(`live=${ENV.liveBase}`);
76
+ console.error(`[voidnet] custom hosts in effect: ${parts.join(' ')} — proceed only if you configured them.`);
77
+ if (process.argv.includes('--yes') || process.env.VOIDNET_ASSUME_YES === '1') {
78
+ console.error('[voidnet] custom hosts pre-approved by flag/env.');
79
+ return;
80
+ }
81
+ if (!process.stdin.isTTY) {
82
+ out.error('custom hosts require --yes or VOIDNET_ASSUME_YES=1 in non-interactive runs. Aborting before any credential leaves this machine.');
83
+ }
84
+ const answer = await new Promise((resolve) => {
85
+ process.stdout.write('Continue against custom hosts? [y/N] ');
86
+ let buf = '';
87
+ process.stdin.setEncoding('utf8');
88
+ process.stdin.once('data', (d) => { buf += d; resolve(buf.trim().toLowerCase()); });
89
+ });
90
+ if (answer !== 'y' && answer !== 'yes') {
91
+ out.error('aborted by user. No credentials sent.');
92
+ }
93
+ }
94
+
64
95
  const KNOWN = new Set(['login', 'logout', 'apps', 'logs', 'keys', 'help', 'whoami', 'status', 'doctor']);
65
96
  if (!cmd || cmd === 'help' || process.argv.includes('--help') || process.argv.includes('-h')) {
66
97
  printHelp(sub || null);
67
- } else if (process.argv.includes('--version') || cmd === 'version') {
98
+ } else if (process.argv.includes('--version') || process.argv.includes('-v') || cmd === 'version') {
68
99
  printVersion();
69
100
  } else if (!KNOWN.has(cmd)) {
70
101
  out.error(`unknown command "${cmd}". Run voidnet --help.`);
@@ -83,7 +114,8 @@ function printHelp(topic) {
83
114
  --env production|development, --base/--auth-base/--live-base URLs
84
115
  VOIDNET_ENV, VOIDNET_MGMT_BASE, VOIDNET_AUTH_BASE, VOIDNET_LIVE_BASE
85
116
  Resolution: flags > VOIDNET_* env > baked default (production).
86
- Config: ~/.config/voidnet/cli.json vs cli.development.json (per env, never shared).`);
117
+ Custom hosts demand consent: interactive runs prompt once, pipes and CI need
118
+ --yes or VOIDNET_ASSUME_YES=1, otherwise nothing credentialed is sent.`);
87
119
  return;
88
120
  }
89
121
  if (topic === 'login') {
@@ -114,7 +146,7 @@ Other commands:
114
146
  version Print CLI version
115
147
 
116
148
  Global flags:
117
- --json, --quiet, --verbose
149
+ --json, --quiet, --verbose, --yes
118
150
 
119
151
  Run voidnet help advanced for environments.`);
120
152
  }
@@ -136,6 +168,7 @@ if (cmd === 'login' && tokenIdx >= 0) {
136
168
  } else if (cmd === 'logout') {
137
169
  const cfg = loadConfig();
138
170
  if (cfg.token) {
171
+ await confirmCustomHosts();
139
172
  try {
140
173
  await fetch(`${ENV.mgmtBase}/api/publisher/v1/auth/revoke-self`, {
141
174
  method: 'POST',
@@ -316,6 +349,7 @@ function openBrowser(url) {
316
349
  }
317
350
 
318
351
  async function browserLogin() {
352
+ await confirmCustomHosts();
319
353
  const [{ randomBytes, createHash }, { createServer }] = await Promise.all([
320
354
  import('node:crypto'),
321
355
  import('node:http'),
package/package.json CHANGED
@@ -1,15 +1,24 @@
1
1
  {
2
2
  "name": "openvoidnet",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "private": false,
5
5
  "description": "Voidnet publisher CLI — manage your Voidnet apps from the terminal with browser-based login.",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://openvoidnet.com",
8
- "repository": {
9
- "type": "git",
10
- "url": "git+ssh://git@github.com/roshaan-akther/void.git",
11
- "directory": "packages/voidnet-cli"
12
- },
8
+ "keywords": [
9
+ "voidnet",
10
+ "mcp",
11
+ "model-context-protocol",
12
+ "llm",
13
+ "openai-compatible",
14
+ "ai-agents",
15
+ "tool-calling",
16
+ "api-client",
17
+ "cli",
18
+ "developer-tools",
19
+ "automation",
20
+ "marketplace"
21
+ ],
13
22
  "type": "module",
14
23
  "bin": {
15
24
  "voidnet": "bin/voidnet.js"
@@ -28,5 +37,14 @@
28
37
  },
29
38
  "engines": {
30
39
  "node": ">=20.9.0"
40
+ },
41
+ "main": "index.js",
42
+ "directories": {
43
+ "test": "test"
44
+ },
45
+ "keywords": [],
46
+ "author": "",
47
+ "bugs": {
48
+ "url": "https://github.com/roshaan-akther/void/issues"
31
49
  }
32
50
  }
package/src/env.js CHANGED
@@ -40,6 +40,14 @@ export function resolveEnv({ argv = process.argv, env = process.env, silent = fa
40
40
  const authBase = flagValue(argv, '--auth-base') || env.VOIDNET_AUTH_BASE || table.authBase;
41
41
  const liveBase = flagValue(argv, '--live-base') || env.VOIDNET_LIVE_BASE || table.liveBase;
42
42
  const configPath = join(homedir(), '.config', 'voidnet', table.configFile);
43
+ // overridden tracks per-host deviation from the baked table. A custom host
44
+ // receives bearer tokens, so deviation always requires explicit consent at
45
+ // use time (see confirmCustomHosts in the binary) — never silent.
46
+ const overridden = {
47
+ mgmt: mgmtBase !== table.mgmtBase,
48
+ auth: authBase !== table.authBase,
49
+ live: liveBase !== table.liveBase,
50
+ };
43
51
  const noteworthy =
44
52
  name !== 'production' ||
45
53
  explicit !== null ||
@@ -51,5 +59,5 @@ export function resolveEnv({ argv = process.argv, env = process.env, silent = fa
51
59
  env.VOIDNET_AUTH_BASE !== undefined ||
52
60
  env.VOIDNET_LIVE_BASE !== undefined;
53
61
  if (!silent) console.log(`[voidnet] env=${name} mgmt=${mgmtBase} auth=${authBase} live=${liveBase}`);
54
- return { name, mgmtBase, authBase, liveBase, configPath, noteworthy };
62
+ return { name, mgmtBase, authBase, liveBase, configPath, noteworthy, overridden };
55
63
  }