Skip to content

Commit 2070c2c

Browse files
authored
feat: require explicit third-party software acceptance before onboarding (#1290)
## Summary - show a third-party software notice before `install.sh` launches onboarding and before direct `nemoclaw onboard` - require explicit `yes` in interactive flows and fail immediately on any other answer - require explicit non-interactive acceptance via `--yes-i-accept-third-party-software` or `NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1` - link users to the official OpenClaw security guidance before continuing - render the security URL as a clickable terminal hyperlink where OSC 8 is supported, while still printing the raw URL - implement the new notice capability in TypeScript under `src/lib` ## Details This is aimed at issue #1213, but the acceptance behavior is stricter than the original issue text. The notice is specifically about third-party software liability and OpenClaw usage, not NemoClaw terms. The acceptance flag and env var are named accordingly. The current notice is intentionally formatted for standard terminal width and uses the approved wording in short, non-wrapping lines. Reference shown to users: - https://docs.openclaw.ai/gateway/security Credit: - builds on prior overlapping notice work in #1222 by @13ernkastel, but extends it to the current acceptance model, non-interactive enforcement, and the shared `install.sh` + `nemoclaw onboard` flow ## Validation - `npm run build:cli` - `npx vitest run test/usage-notice.test.js test/cli.test.js test/install-preflight.test.js` - `npx eslint bin/lib/usage-notice.js test/usage-notice.test.js` - `npx tsc -p jsconfig.json --noEmit` ## Note The full local git hook suite is currently blocked in this environment by an unrelated `src/lib/preflight.test.ts` localhost listen failure (`EPERM` on `127.0.0.1`). I did not change that area in this branch. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Onboarding and installer now require explicit acceptance of third‑party software terms before proceeding; onboarding exits early if consent is not given. * Interactive mode displays a bundled usage notice and prompts users to type "yes"; acceptance is persisted per user. * Non‑interactive installs allow scripted acceptance via a new explicit acceptance mechanism. * **Documentation** * CLI help, installer messages, and docs updated to document the new non‑interactive acceptance option and TTY guidance. * **Tests** * Added and updated tests and e2e scripts to cover acceptance flows and enforcement. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 7e25e99 commit 2070c2c

22 files changed

+645
-43
lines changed

.github/workflows/nightly-e2e.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ jobs:
4949
env:
5050
NVIDIA_API_KEY: ${{ secrets.NVIDIA_API_KEY }}
5151
NEMOCLAW_NON_INTERACTIVE: "1"
52+
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
5253
NEMOCLAW_SANDBOX_NAME: "e2e-nightly"
5354
NEMOCLAW_RECREATE_SANDBOX: "1"
5455
GITHUB_TOKEN: ${{ github.token }}
@@ -80,6 +81,7 @@ jobs:
8081
# Non-interactive install (expect-driven Phase 3 optional). Runner has no expect; Phase 5e TUI skips if expect is absent.
8182
RUN_E2E_CLOUD_EXPERIMENTAL_INTERACTIVE_INSTALL: "0"
8283
NEMOCLAW_NON_INTERACTIVE: "1"
84+
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
8385
NEMOCLAW_RECREATE_SANDBOX: "1"
8486
NEMOCLAW_POLICY_MODE: "custom"
8587
NEMOCLAW_POLICY_PRESETS: "npm,pypi"
@@ -171,6 +173,7 @@ jobs:
171173
timeout-minutes: 60
172174
env:
173175
NEMOCLAW_NON_INTERACTIVE: "1"
176+
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
174177
NEMOCLAW_SANDBOX_NAME: "e2e-gpu-ollama"
175178
NEMOCLAW_RECREATE_SANDBOX: "1"
176179
NEMOCLAW_PROVIDER: "ollama"

bin/lib/onboard.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const registry = require("./registry");
5353
const nim = require("./nim");
5454
const onboardSession = require("./onboard-session");
5555
const policies = require("./policies");
56+
const { ensureUsageNoticeConsent } = require("./usage-notice");
5657
const { checkPortAvailable, ensureSwap, getMemoryInfo } = require("./preflight");
5758

5859
// Typed modules (compiled from src/lib/*.ts → dist/lib/*.js)
@@ -3450,6 +3451,14 @@ async function onboard(opts = {}) {
34503451
NON_INTERACTIVE = opts.nonInteractive || process.env.NEMOCLAW_NON_INTERACTIVE === "1";
34513452
delete process.env.OPENSHELL_GATEWAY;
34523453
const resume = opts.resume === true;
3454+
const noticeAccepted = await ensureUsageNoticeConsent({
3455+
nonInteractive: isNonInteractive(),
3456+
acceptedByFlag: opts.acceptThirdPartySoftware === true,
3457+
writeLine: console.error,
3458+
});
3459+
if (!noticeAccepted) {
3460+
process.exit(1);
3461+
}
34533462
const lockResult = onboardSession.acquireOnboardLock(
34543463
`nemoclaw onboard${resume ? " --resume" : ""}${isNonInteractive() ? " --non-interactive" : ""}`,
34553464
);

bin/lib/usage-notice.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
// Thin re-export shim — the implementation lives in src/lib/usage-notice.ts,
5+
// compiled to dist/lib/usage-notice.js.
6+
const usageNotice = require("../../dist/lib/usage-notice");
7+
8+
if (require.main === module) {
9+
usageNotice.cli().catch((error) => {
10+
console.error(error?.message || String(error));
11+
process.exit(1);
12+
});
13+
}
14+
15+
module.exports = usageNotice;

bin/lib/usage-notice.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"version": "2026-04-01b",
3+
"title": "Third-Party Software Notice - NemoClaw Installer",
4+
"referenceUrl": "https://docs.openclaw.ai/gateway/security",
5+
"body": [
6+
"NemoClaw is licensed under Apache 2.0 and automatically",
7+
"retrieves, accesses or interacts with third-party software",
8+
"and materials, including by deploying OpenClaw in an",
9+
"OpenShell sandbox. Those retrieved materials are not",
10+
"distributed with this software and are governed solely",
11+
"by separate terms, conditions and licenses.",
12+
"",
13+
"You are solely responsible for finding, reviewing and",
14+
"complying with all applicable terms, conditions, and",
15+
"licenses, and for verifying the security, integrity and",
16+
"suitability of any retrieved materials for your specific",
17+
"use case.",
18+
"",
19+
"This software is provided \"AS IS\", without warranty of",
20+
"any kind. The author makes no representations or",
21+
"warranties regarding any third-party software, and",
22+
"assumes no liability for any losses, damages, liabilities",
23+
"or legal consequences from your use or inability to use",
24+
"this software or any retrieved materials. Use this",
25+
"software and the retrieved materials at your own risk.",
26+
"",
27+
"OpenClaw security guidance",
28+
"https://docs.openclaw.ai/gateway/security"
29+
],
30+
"links": [],
31+
"interactivePrompt": "Type 'yes' to accept the NemoClaw license and and third-party software notice and continue [no]: "
32+
}

bin/nemoclaw.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ const { parseGatewayInference } = require("./lib/inference-config");
4545
const { getVersion } = require("./lib/version");
4646
const onboardSession = require("./lib/onboard-session");
4747
const { parseLiveSandboxNames } = require("./lib/runtime-recovery");
48+
const { NOTICE_ACCEPT_ENV, NOTICE_ACCEPT_FLAG } = require("./lib/usage-notice");
4849

4950
// ── Global commands ──────────────────────────────────────────────
5051

@@ -622,16 +623,20 @@ function exitWithSpawnResult(result) {
622623

623624
async function onboard(args) {
624625
const { onboard: runOnboard } = require("./lib/onboard");
625-
const allowedArgs = new Set(["--non-interactive", "--resume"]);
626+
const allowedArgs = new Set(["--non-interactive", "--resume", NOTICE_ACCEPT_FLAG]);
626627
const unknownArgs = args.filter((arg) => !allowedArgs.has(arg));
627628
if (unknownArgs.length > 0) {
628629
console.error(` Unknown onboard option(s): ${unknownArgs.join(", ")}`);
629-
console.error(" Usage: nemoclaw onboard [--non-interactive] [--resume]");
630+
console.error(
631+
` Usage: nemoclaw onboard [--non-interactive] [--resume] [${NOTICE_ACCEPT_FLAG}]`,
632+
);
630633
process.exit(1);
631634
}
632635
const nonInteractive = args.includes("--non-interactive");
633636
const resume = args.includes("--resume");
634-
await runOnboard({ nonInteractive, resume });
637+
const acceptThirdPartySoftware =
638+
args.includes(NOTICE_ACCEPT_FLAG) || String(process.env[NOTICE_ACCEPT_ENV] || "") === "1";
639+
await runOnboard({ nonInteractive, resume, acceptThirdPartySoftware });
635640
}
636641

637642
async function setup(args = []) {
@@ -1154,6 +1159,7 @@ function help() {
11541159
11551160
${G}Getting Started:${R}
11561161
${B}nemoclaw onboard${R} Configure inference endpoint and credentials
1162+
${D}(non-interactive: ${NOTICE_ACCEPT_FLAG} or ${NOTICE_ACCEPT_ENV}=1)${R}
11571163
nemoclaw setup-spark Set up on DGX Spark ${D}(fixes cgroup v2 + Docker)${R}
11581164
11591165
${G}Sandbox Management:${R}

docs/reference/commands.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,18 @@ Supported non-experimental choices include NVIDIA Endpoints, OpenAI, Anthropic,
6969
Credentials are stored in `~/.nemoclaw/credentials.json`.
7070
The legacy `nemoclaw setup` command is deprecated; use `nemoclaw onboard` instead.
7171

72+
For non-interactive onboarding, you must explicitly accept the third-party software notice:
73+
74+
```console
75+
$ nemoclaw onboard --non-interactive --yes-i-accept-third-party-software
76+
```
77+
78+
or:
79+
80+
```console
81+
$ NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 nemoclaw onboard --non-interactive
82+
```
83+
7284
The wizard prompts for a sandbox name.
7385
Names must follow RFC 1123 subdomain rules: lowercase alphanumeric characters and hyphens only, and must start and end with an alphanumeric character.
7486
Uppercase letters are automatically lowercased.

install.sh

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,10 +217,12 @@ usage() {
217217
printf " curl -fsSL https://www.nvidia.com/nemoclaw.sh | bash -s -- [options]\n\n"
218218
printf " ${C_DIM}Options:${C_RESET}\n"
219219
printf " --non-interactive Skip prompts (uses env vars / defaults)\n"
220+
printf " --yes-i-accept-third-party-software Accept the third-party software notice in non-interactive mode\n"
220221
printf " --version, -v Print installer version and exit\n"
221222
printf " --help, -h Show this help message and exit\n\n"
222223
printf " ${C_DIM}Environment:${C_RESET}\n"
223224
printf " NVIDIA_API_KEY API key (skips credential prompt)\n"
225+
printf " NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE=1 Same as --yes-i-accept-third-party-software\n"
224226
printf " NEMOCLAW_NON_INTERACTIVE=1 Same as --non-interactive\n"
225227
printf " NEMOCLAW_SANDBOX_NAME Sandbox name to create/use\n"
226228
printf " NEMOCLAW_RECREATE_SANDBOX=1 Recreate an existing sandbox\n"
@@ -237,6 +239,27 @@ usage() {
237239
printf "\n"
238240
}
239241

242+
show_usage_notice() {
243+
local -a notice_cmd=(node "${SCRIPT_DIR}/bin/lib/usage-notice.js")
244+
if [ "${NON_INTERACTIVE:-}" = "1" ]; then
245+
notice_cmd+=(--non-interactive)
246+
if [ "${ACCEPT_THIRD_PARTY_SOFTWARE:-}" = "1" ]; then
247+
notice_cmd+=(--yes-i-accept-third-party-software)
248+
fi
249+
"${notice_cmd[@]}"
250+
elif [ -t 0 ]; then
251+
"${notice_cmd[@]}"
252+
elif exec 3</dev/tty; then
253+
info "Installer stdin is piped; attaching the usage notice to /dev/tty…"
254+
local status=0
255+
"${notice_cmd[@]}" <&3 || status=$?
256+
exec 3<&-
257+
return "$status"
258+
else
259+
error "Interactive third-party software acceptance requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1 with --yes-i-accept-third-party-software."
260+
fi
261+
}
262+
240263
# spin "label" cmd [args...]
241264
# Runs a command in the background, showing a braille spinner until it exits.
242265
# Stdout/stderr are captured; dumped only on failure.
@@ -806,6 +829,7 @@ verify_nemoclaw() {
806829
# 5. Onboard
807830
# ---------------------------------------------------------------------------
808831
run_onboard() {
832+
show_usage_notice
809833
info "Running nemoclaw onboard…"
810834
local -a onboard_cmd=(onboard)
811835
if command_exists node && [[ -f "${HOME}/.nemoclaw/onboard-session.json" ]]; then
@@ -827,6 +851,9 @@ run_onboard() {
827851
fi
828852
if [ "${NON_INTERACTIVE:-}" = "1" ]; then
829853
onboard_cmd+=(--non-interactive)
854+
if [ "${ACCEPT_THIRD_PARTY_SOFTWARE:-}" = "1" ]; then
855+
onboard_cmd+=(--yes-i-accept-third-party-software)
856+
fi
830857
nemoclaw "${onboard_cmd[@]}"
831858
elif [ -t 0 ]; then
832859
nemoclaw "${onboard_cmd[@]}"
@@ -837,7 +864,7 @@ run_onboard() {
837864
exec 3<&-
838865
return "$status"
839866
else
840-
error "Interactive onboarding requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1."
867+
error "Interactive onboarding requires a TTY. Re-run in a terminal or set NEMOCLAW_NON_INTERACTIVE=1 with --yes-i-accept-third-party-software."
841868
fi
842869
}
843870

@@ -880,9 +907,11 @@ post_install_message() {
880907
main() {
881908
# Parse flags
882909
NON_INTERACTIVE=""
910+
ACCEPT_THIRD_PARTY_SOFTWARE=""
883911
for arg in "$@"; do
884912
case "$arg" in
885913
--non-interactive) NON_INTERACTIVE=1 ;;
914+
--yes-i-accept-third-party-software) ACCEPT_THIRD_PARTY_SOFTWARE=1 ;;
886915
--version | -v)
887916
printf "nemoclaw-installer v%s\n" "$NEMOCLAW_VERSION"
888917
exit 0
@@ -899,7 +928,9 @@ main() {
899928
done
900929
# Also honor env var
901930
NON_INTERACTIVE="${NON_INTERACTIVE:-${NEMOCLAW_NON_INTERACTIVE:-}}"
931+
ACCEPT_THIRD_PARTY_SOFTWARE="${ACCEPT_THIRD_PARTY_SOFTWARE:-${NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE:-}}"
902932
export NEMOCLAW_NON_INTERACTIVE="${NON_INTERACTIVE}"
933+
export NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE="${ACCEPT_THIRD_PARTY_SOFTWARE}"
903934

904935
_INSTALL_START=$SECONDS
905936
print_banner

0 commit comments

Comments
 (0)