aquaman-proxy 0.16.0 → 0.17.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/README.md +9 -0
- package/dist/cli/index.js +171 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/types.d.ts +10 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/utils/config.d.ts.map +1 -1
- package/dist/core/utils/config.js +9 -1
- package/dist/core/utils/config.js.map +1 -1
- package/dist/openshell/credential-driver.d.ts +117 -0
- package/dist/openshell/credential-driver.d.ts.map +1 -0
- package/dist/openshell/credential-driver.js +181 -0
- package/dist/openshell/credential-driver.js.map +1 -0
- package/dist/openshell/grpc-server.d.ts +23 -0
- package/dist/openshell/grpc-server.d.ts.map +1 -0
- package/dist/openshell/grpc-server.js +122 -0
- package/dist/openshell/grpc-server.js.map +1 -0
- package/dist/openshell/integration.d.ts +37 -0
- package/dist/openshell/integration.d.ts.map +1 -0
- package/dist/openshell/integration.js +69 -0
- package/dist/openshell/integration.js.map +1 -0
- package/package.json +12 -3
- package/proto/openshell/README.md +9 -0
- package/proto/openshell/credential_driver.proto +146 -0
- package/proto/openshell/datamodel.proto +128 -0
- package/proto/openshell/extension.proto +34 -0
- package/proto/openshell/options.proto +36 -0
package/README.md
CHANGED
|
@@ -99,6 +99,15 @@ Scoped since v0.15.0, because it hands out values rather than injecting them:
|
|
|
99
99
|
|
|
100
100
|
`aquaman get <aquaman://service/key>` (v0.16.0+) is the command-line form, for tools that take a command printing a secret: Docker Sandboxes (`sbx secret set <svc> --command`), Codex (`model_providers.<id>.auth.command`), Claude Code (`apiKeyHelper`), OpenClaw exec secret providers and Hermes command secret sources. It only talks to the running daemon, so the same scope applies and every read is audited. It refuses to print to an interactive terminal without `--show`, and prints no trailing newline when another program reads it. The value goes to the program that runs the command.
|
|
101
101
|
|
|
102
|
+
## OpenShell credential driver (v0.17.0+)
|
|
103
|
+
|
|
104
|
+
`aquaman daemon` can serve NVIDIA OpenShell's external credential driver protocol (`credential_driver.proto`, extension protocol 1.0) on `<configDir>/openshell.sock` (0600). Enable it with `aquaman openshell setup`, which prints the gateway TOML.
|
|
105
|
+
|
|
106
|
+
- **Reference mode:** `openshell provider create --credential KEY=aquaman://svc/key`. The ref must be declared (`aquaman broker allow`) and exist in the vault; nothing is stored, the ref is the handle, and deleting the provider never deletes the vault item.
|
|
107
|
+
- **Copy mode:** a plain value is written to the vault under the `openshell` service.
|
|
108
|
+
- Every resolve is audited as a `read` (agent `openshell`), without the value. Handles are interpreted by namespace, never by gateway-supplied metadata, so a reference can't bypass the allow-list.
|
|
109
|
+
- `@grpc/grpc-js` and `@grpc/proto-loader` are optional peer dependencies, loaded only when the driver is on. The vendored protocol files live in `proto/openshell/`.
|
|
110
|
+
|
|
102
111
|
## Documentation
|
|
103
112
|
|
|
104
113
|
- **[Root README](https://github.com/tech4242/aquaman#readme)**: value prop, three-path Quick Start, security model
|
package/dist/cli/index.js
CHANGED
|
@@ -419,6 +419,14 @@ program
|
|
|
419
419
|
}
|
|
420
420
|
lines.push('');
|
|
421
421
|
}
|
|
422
|
+
const os_ = byName.get('openshell');
|
|
423
|
+
if (os_) {
|
|
424
|
+
lines.push(aqua('NVIDIA OpenShell integration'));
|
|
425
|
+
for (const sub of helper.visibleCommands(os_).filter((s) => s.name() !== 'help')) {
|
|
426
|
+
lines.push(renderRow(`openshell ${sub.name()}`, sub.description() || ''));
|
|
427
|
+
}
|
|
428
|
+
lines.push('');
|
|
429
|
+
}
|
|
422
430
|
// --- Other ---
|
|
423
431
|
const helpCmd = byName.get('help');
|
|
424
432
|
if (helpCmd) {
|
|
@@ -996,10 +1004,27 @@ program
|
|
|
996
1004
|
}
|
|
997
1005
|
});
|
|
998
1006
|
await credentialProxy.start();
|
|
1007
|
+
// OpenShell external credential driver (v0.17.0+). Opt-in; a failure here
|
|
1008
|
+
// is reported but never takes the proxy down.
|
|
1009
|
+
let openshellDriver;
|
|
1010
|
+
let openshellDriverError;
|
|
1011
|
+
if (config.openshell?.driver?.enabled) {
|
|
1012
|
+
try {
|
|
1013
|
+
const { startOpenShellDriver } = await import('../openshell/integration.js');
|
|
1014
|
+
openshellDriver = await startOpenShellDriver({ config, store: credentialStore, scope: brokerScope, audit: auditLogger, version: VERSION });
|
|
1015
|
+
}
|
|
1016
|
+
catch (err) {
|
|
1017
|
+
openshellDriverError = err instanceof Error ? err.message : String(err);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
999
1020
|
// Write PID file
|
|
1000
1021
|
writePidFile();
|
|
1001
1022
|
console.log(`Credential proxy: ${socketPath}`);
|
|
1002
1023
|
console.log(`Credential broker: ${describeBroker(brokerScope)}`);
|
|
1024
|
+
if (openshellDriver)
|
|
1025
|
+
console.log(`OpenShell credential driver: ${openshellDriver.socketPath}`);
|
|
1026
|
+
if (openshellDriverError)
|
|
1027
|
+
console.error(` ⚠ OpenShell credential driver not started: ${openshellDriverError}`);
|
|
1003
1028
|
if (brokerScope?.declarationError()) {
|
|
1004
1029
|
console.error(` ⚠ ${brokerScope.declarationError()} — refs declared there are refused until it parses`);
|
|
1005
1030
|
}
|
|
@@ -1011,6 +1036,7 @@ program
|
|
|
1011
1036
|
// Handle shutdown
|
|
1012
1037
|
const shutdown = async () => {
|
|
1013
1038
|
console.log('\nShutting down daemon...');
|
|
1039
|
+
await openshellDriver?.stop();
|
|
1014
1040
|
await credentialProxy.stop();
|
|
1015
1041
|
removePidFile();
|
|
1016
1042
|
try {
|
|
@@ -2974,6 +3000,151 @@ policy
|
|
|
2974
3000
|
console.log(` \u2717 DENIED by rule: ${rule.method} ${rule.path} \u2192 ${rule.action}`);
|
|
2975
3001
|
}
|
|
2976
3002
|
});
|
|
3003
|
+
// ── aquaman openshell …: NVIDIA OpenShell external credential driver (v0.17.0+) ──
|
|
3004
|
+
const openshell = program
|
|
3005
|
+
.command('openshell')
|
|
3006
|
+
.description('NVIDIA OpenShell integration: aquaman as the gateway credential driver');
|
|
3007
|
+
/** Edit config.yaml `openshell.driver` in the raw file so env overrides are never persisted. */
|
|
3008
|
+
function updateOpenShellDriverConfig(enabled, socketPath) {
|
|
3009
|
+
ensureConfigDir();
|
|
3010
|
+
const configPath = getConfigPath();
|
|
3011
|
+
let raw = {};
|
|
3012
|
+
if (fs.existsSync(configPath)) {
|
|
3013
|
+
const parsed = yamlParse(fs.readFileSync(configPath, 'utf-8'));
|
|
3014
|
+
if (parsed && typeof parsed === 'object')
|
|
3015
|
+
raw = parsed;
|
|
3016
|
+
}
|
|
3017
|
+
const current = raw.openshell?.driver && typeof raw.openshell.driver === 'object' ? raw.openshell.driver : {};
|
|
3018
|
+
raw.openshell = { ...(raw.openshell && typeof raw.openshell === 'object' ? raw.openshell : {}), driver: { ...current, enabled } };
|
|
3019
|
+
if (socketPath)
|
|
3020
|
+
raw.openshell.driver.socketPath = socketPath;
|
|
3021
|
+
fs.writeFileSync(configPath, yamlStringify(raw), { encoding: 'utf-8', mode: 0o600 });
|
|
3022
|
+
}
|
|
3023
|
+
openshell
|
|
3024
|
+
.command('setup')
|
|
3025
|
+
.description('Serve the OpenShell credential driver from `aquaman daemon` and print the gateway config')
|
|
3026
|
+
.option('--socket <path>', 'Absolute Unix socket path for the driver (default: <configDir>/openshell.sock)')
|
|
3027
|
+
.option('--disable', 'Turn the driver off again')
|
|
3028
|
+
.action(async (opts) => {
|
|
3029
|
+
const { grpcAvailable, gatewayConfigSnippet, openshellDriverSocketPath, MAX_SOCKET_PATH } = await import('../openshell/integration.js');
|
|
3030
|
+
const { GRPC_INSTALL_HINT } = await import('../openshell/grpc-server.js');
|
|
3031
|
+
if (opts.disable) {
|
|
3032
|
+
updateOpenShellDriverConfig(false);
|
|
3033
|
+
console.log('OpenShell credential driver disabled. Restart the daemon: aquaman stop && aquaman daemon');
|
|
3034
|
+
console.log('Remove the [openshell.credential_drivers.aquaman] table from your gateway config too.');
|
|
3035
|
+
return;
|
|
3036
|
+
}
|
|
3037
|
+
if (opts.socket && !path.isAbsolute(opts.socket)) {
|
|
3038
|
+
console.error('--socket must be an absolute path (the gateway config needs one).');
|
|
3039
|
+
process.exit(1);
|
|
3040
|
+
}
|
|
3041
|
+
updateOpenShellDriverConfig(true, opts.socket);
|
|
3042
|
+
const socketPath = openshellDriverSocketPath(loadConfig());
|
|
3043
|
+
console.log(`OpenShell credential driver enabled in ${getConfigPath()}`);
|
|
3044
|
+
if (Buffer.byteLength(socketPath) > MAX_SOCKET_PATH) {
|
|
3045
|
+
console.error(` ⚠ ${socketPath} is longer than the ${MAX_SOCKET_PATH}-byte Unix socket limit. Rerun with --socket <shorter path>.`);
|
|
3046
|
+
}
|
|
3047
|
+
if (!(await grpcAvailable())) {
|
|
3048
|
+
console.log(`\n The driver needs two optional packages: ${aqua(GRPC_INSTALL_HINT)}`);
|
|
3049
|
+
}
|
|
3050
|
+
console.log('\n1. Add this to your OpenShell gateway config (TOML):\n');
|
|
3051
|
+
console.log(gatewayConfigSnippet(socketPath).split('\n').map((l) => ` ${l}`).join('\n'));
|
|
3052
|
+
console.log('\n2. Restart aquaman, then the gateway:');
|
|
3053
|
+
console.log(' aquaman stop; aquaman daemon');
|
|
3054
|
+
console.log('\n3. Create providers by reference. The secret stays in your vault; nothing is copied:');
|
|
3055
|
+
console.log(` ${aqua('aquaman broker allow aquaman://anthropic/api_key')}`);
|
|
3056
|
+
console.log(` openshell provider create --name claude --type anthropic --credential ANTHROPIC_API_KEY=aquaman://anthropic/api_key`);
|
|
3057
|
+
console.log('\n A plain value (--credential KEY=sk-...) is stored in your vault under the "openshell" service instead.');
|
|
3058
|
+
console.log(`\nCheck everything with: ${aqua('aquaman openshell doctor')}`);
|
|
3059
|
+
});
|
|
3060
|
+
openshell
|
|
3061
|
+
.command('doctor')
|
|
3062
|
+
.description('Check the OpenShell credential driver: packages, config, socket, declared refs')
|
|
3063
|
+
.option('--gateway-config <path>', 'Also check an OpenShell gateway TOML file points at this driver')
|
|
3064
|
+
.action(async (opts) => {
|
|
3065
|
+
const net = await import('node:net');
|
|
3066
|
+
const { grpcAvailable, openshellDriverSocketPath, MAX_SOCKET_PATH } = await import('../openshell/integration.js');
|
|
3067
|
+
const { GRPC_INSTALL_HINT } = await import('../openshell/grpc-server.js');
|
|
3068
|
+
const config = loadConfig();
|
|
3069
|
+
const socketPath = openshellDriverSocketPath(config);
|
|
3070
|
+
let failed = 0;
|
|
3071
|
+
const pass = (m) => console.log(` ✓ ${m}`);
|
|
3072
|
+
const fail = (m, fix) => { failed++; console.log(` ✗ ${m}`); if (fix)
|
|
3073
|
+
console.log(` → ${fix}`); };
|
|
3074
|
+
console.log(`\n ${aqua('OpenShell credential driver')}\n`);
|
|
3075
|
+
if (await grpcAvailable())
|
|
3076
|
+
pass('gRPC packages installed (@grpc/grpc-js, @grpc/proto-loader)');
|
|
3077
|
+
else
|
|
3078
|
+
fail('gRPC packages missing', GRPC_INSTALL_HINT);
|
|
3079
|
+
if (config.openshell?.driver?.enabled)
|
|
3080
|
+
pass('Driver enabled in config.yaml');
|
|
3081
|
+
else
|
|
3082
|
+
fail('Driver not enabled', 'aquaman openshell setup');
|
|
3083
|
+
if (Buffer.byteLength(socketPath) <= MAX_SOCKET_PATH)
|
|
3084
|
+
pass(`Socket path ${socketPath}`);
|
|
3085
|
+
else
|
|
3086
|
+
fail(`Socket path over ${MAX_SOCKET_PATH} bytes: ${socketPath}`, 'aquaman openshell setup --socket <shorter absolute path>');
|
|
3087
|
+
let isSocket = false;
|
|
3088
|
+
try {
|
|
3089
|
+
isSocket = fs.statSync(socketPath).isSocket();
|
|
3090
|
+
}
|
|
3091
|
+
catch { /* absent */ }
|
|
3092
|
+
if (!isSocket) {
|
|
3093
|
+
fail('Driver socket not present (daemon not running, or started before setup)', 'aquaman stop; aquaman daemon');
|
|
3094
|
+
}
|
|
3095
|
+
else {
|
|
3096
|
+
const mode = fs.statSync(socketPath).mode & 0o777;
|
|
3097
|
+
if (mode === 0o600)
|
|
3098
|
+
pass('Socket is owner-only (0600)');
|
|
3099
|
+
else
|
|
3100
|
+
fail(`Socket mode is ${mode.toString(8)}, expected 600`, 'aquaman stop; aquaman daemon');
|
|
3101
|
+
const reachable = await new Promise((resolve) => {
|
|
3102
|
+
const c = net.connect(socketPath, () => { c.end(); resolve(true); });
|
|
3103
|
+
c.on('error', () => resolve(false));
|
|
3104
|
+
setTimeout(() => { c.destroy(); resolve(false); }, 2000);
|
|
3105
|
+
});
|
|
3106
|
+
if (reachable)
|
|
3107
|
+
pass('Driver socket accepts connections');
|
|
3108
|
+
else
|
|
3109
|
+
fail('Driver socket does not accept connections', 'aquaman stop; aquaman daemon');
|
|
3110
|
+
}
|
|
3111
|
+
const scope = daemonBrokerScope(config);
|
|
3112
|
+
if (!scope) {
|
|
3113
|
+
fail('Broker disabled (broker.enabled: false): references cannot resolve', 'set broker.enabled: true in config.yaml');
|
|
3114
|
+
}
|
|
3115
|
+
else {
|
|
3116
|
+
const n = scope.declared().length;
|
|
3117
|
+
if (n > 0)
|
|
3118
|
+
pass(`${n} declared ref${n === 1 ? '' : 's'} usable by reference (aquaman broker list)`);
|
|
3119
|
+
else
|
|
3120
|
+
console.log(' • No declared refs yet: reference-mode providers will be refused until you run aquaman broker allow <ref>');
|
|
3121
|
+
}
|
|
3122
|
+
if (opts.gatewayConfig) {
|
|
3123
|
+
let toml = '';
|
|
3124
|
+
try {
|
|
3125
|
+
toml = fs.readFileSync(opts.gatewayConfig, 'utf-8');
|
|
3126
|
+
}
|
|
3127
|
+
catch {
|
|
3128
|
+
fail(`Cannot read ${opts.gatewayConfig}`);
|
|
3129
|
+
}
|
|
3130
|
+
if (toml) {
|
|
3131
|
+
const listsDriver = /credential_drivers\s*=\s*\[[^\]]*"aquaman"[^\]]*\]/.test(toml);
|
|
3132
|
+
const table = /\[openshell\.credential_drivers\.aquaman\]([\s\S]*?)(?=\n\[|$)/.exec(toml)?.[1] ?? '';
|
|
3133
|
+
const uds = /transport\s*=\s*"uds"/.test(table);
|
|
3134
|
+
const sock = /socket_path\s*=\s*"([^"]*)"/.exec(table)?.[1];
|
|
3135
|
+
if (listsDriver && uds && sock === socketPath)
|
|
3136
|
+
pass(`Gateway config ${opts.gatewayConfig} points at this driver`);
|
|
3137
|
+
else
|
|
3138
|
+
fail(`Gateway config ${opts.gatewayConfig} does not point at this driver`, 'paste the snippet from: aquaman openshell setup');
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
console.log('');
|
|
3142
|
+
if (failed > 0) {
|
|
3143
|
+
console.log(` ${failed} check${failed === 1 ? '' : 's'} failed.\n`);
|
|
3144
|
+
process.exit(1);
|
|
3145
|
+
}
|
|
3146
|
+
console.log(' All checks passed.\n');
|
|
3147
|
+
});
|
|
2977
3148
|
// Credential broker scope (v0.15.0+)
|
|
2978
3149
|
const broker = program
|
|
2979
3150
|
.command('broker')
|