@axonflow/openclaw 2.0.0 → 2.0.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/CHANGELOG.md CHANGED
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.1] - 2026-04-30 — Restore ClawHub install + explicit Community-SaaS consent surface
4
+
5
+ ClawHub's static-analysis scanner blocked install of `@axonflow/openclaw@2.0.0` because the telemetry and Community-SaaS bootstrap modules co-located `process.env.*` access and `fs.readFileSync(...)` calls with the outbound `fetch(...)` in the same compiled file — a pattern the scanner heuristically flags as credential-harvesting / potential data exfiltration. This release restores a clean install path on every supported OpenClaw host, adds a real opt-out for Community-SaaS auto-registration, and ships a CI gate so this class of regression cannot recur.
6
+
7
+ ### Added
8
+
9
+ - **`AXONFLOW_COMMUNITY_SAAS=0` opt-out** for the default Community-SaaS auto-registration. When set (also accepts `false`, `off`, `no`), the plugin loads but does not POST to `try.getaxonflow.com/api/v1/register` and does not write `try-registration.json`. Operators who want explicit control over outbound traffic — air-gapped labs, regulated networks — can now turn the auto-bootstrap off without removing the plugin.
10
+ - **First-load Community-SaaS consent disclosure banner.** Before the registration POST fires, the plugin emits a warn-level log line via the OpenClaw plugin logger listing exactly what gets sent off-host (tool name + arguments, outbound message bodies), what does not (LLM provider keys, conversation history outside governed tools), and how to opt out. Banner shows once per machine; presence of the disclosure stamp prevents re-warning on subsequent loads.
11
+ - **Pre-publish security scan gate.** `npm run scan` packs the plugin, extracts the tarball into an isolated state directory, and runs the official OpenClaw scanner against the exact artifact ClawHub re-scans at publish time. A new `.github/workflows/security-scan.yml` runs the same script PR-blocking on every change to `src/`, `dist/`, `package.json`, `openclaw.plugin.json`. Catches scanner regressions before they ship instead of after.
12
+ - **`envVars` and `runtimeBehavior` declarations** in `openclaw.plugin.json`. Documents the four user-facing environment variables (`AXONFLOW_TELEMETRY`, `AXONFLOW_COMMUNITY_SAAS`, `AXONFLOW_CACHE_DIR`, `AXONFLOW_CONFIG_DIR`), the auto-bootstrap data flow, the four persisted files and their permission modes. Registry metadata now matches what the code actually does.
13
+
14
+ ### Changed
15
+
16
+ - **Telemetry module split into `telemetry.ts` + `telemetry-context.ts`.** Environment reads (harness override) and stamp-file reads/writes live in the context module; the network-sending module imports plain values. Behaviour is identical to v2.0.0; only the on-disk module boundary moved. Same change applied to `community-saas-bootstrap.ts` + new `community-saas-context.ts`.
17
+ - **`README.md` rewritten** around a new **"Where your data goes"** section. Replaces the previous data-locality paragraph (which was accurate before v2.0.0 made Community SaaS the default but became misleading after) with three explicit deployment modes: default Community SaaS (what's sent off-host, link to the trial-server disclosure page), self-hosted (your own AxonFlow), and air-gapped (`AXONFLOW_COMMUNITY_SAAS=0` + `AXONFLOW_TELEMETRY=off` = zero outbound). Cross-links the [Try AxonFlow — Free Trial Server](https://docs.getaxonflow.com/docs/deployment/community-saas/) docs page so users can read the full Community SaaS terms.
18
+ - **Removed** the legacy `showCommunitySaasDisclosureOnce` info-level banner that fired *after* the connection was established. The new warn-level banner fires *before* the registration POST, with explicit data-flow disclosure and opt-out instructions, so the consent surface is real rather than after-the-fact.
19
+
20
+ ### Fixed
21
+
22
+ - **`clawhub:@axonflow/openclaw` install no longer blocked** by the host static-analysis scanner on OpenClaw `>=2026.4.15`. Verified with the local `openclaw plugins install` against the packed tarball: scanner reports `0 criticals, 0 warnings`.
23
+
24
+ ### Security
25
+
26
+ - The OpenClaw `>=2026.4.15` peer floor remains in place — it is a real CVE floor (Feishu webhook + card-action validation fail-open in OpenClaw `<2026.4.15`, [GHSA-xh72-v6v9-mwhc](https://github.com/getaxonflow/axonflow-openclaw-plugin/security/advisories/GHSA-cqmh-pcgr-q42f)) and is not relaxed by this release. Anyone running an older OpenClaw should upgrade their host.
27
+
28
+
3
29
  ## [2.0.0] - 2026-04-29 — Production, quality, and security hardening — upgrade encouraged
4
30
 
5
31
  **Upgrade strongly recommended.** Over the past month we've shipped substantial production, quality, and security hardening across the AxonFlow plugin and platform — upgrade to the latest version for a more secure, reliable, and bug-free experience.
package/README.md CHANGED
@@ -23,7 +23,61 @@ OpenClaw is a strong agent runtime. It is also a serious production security pro
23
23
 
24
24
  OpenClaw handles agent runtime, MCP connectivity, channels, and tool execution. It was never intended to be the place you enforce governance. This plugin adds the governance layer on top, so OpenClaw keeps doing what it does well and AxonFlow takes over the "is this allowed, should this redact, who approved, where is the audit record" questions.
25
25
 
26
- **AxonFlow governs. OpenClaw orchestrates. Your data stays on your infrastructure.** No LLM provider keys leave your machine — OpenClaw still makes every LLM call; AxonFlow only evaluates policies and records audit trails.
26
+ **AxonFlow governs. OpenClaw orchestrates.** OpenClaw still makes every LLM call; AxonFlow only evaluates policies and records audit trails. LLM provider keys never leave your machine. Where the rest of your data goes tool inputs, message bodies depends on which deployment mode you pick. See **[Where your data goes](#where-your-data-goes)** below.
27
+
28
+ ---
29
+
30
+ ## Where your data goes
31
+
32
+ The plugin governs tool calls and outbound messages by sending each one to an AxonFlow endpoint for policy evaluation + audit. The endpoint can be the AxonFlow Community SaaS (default, zero-config), a self-hosted AxonFlow instance you run, or nothing at all if you opt out.
33
+
34
+ ### Default: AxonFlow Community SaaS
35
+
36
+ If you install the plugin without setting `pluginConfig.endpoint`, it auto-registers with **`try.getaxonflow.com`** (Community SaaS) on first load and sends governed traffic there. The first-load disclosure banner surfaces this in your plugin logs before the registration POST fires.
37
+
38
+ | What goes to `try.getaxonflow.com` | What does NOT |
39
+ |---|---|
40
+ | Tool name + arguments before each governed call | LLM provider API keys |
41
+ | Outbound message bodies before delivery (PII/secret scan) | OpenClaw conversation history outside governed tools |
42
+ | Anonymous 7-day heartbeat (plugin version, OS, runtime) | Files outside the OpenClaw runtime |
43
+
44
+ Community SaaS is intended for evaluation and prototyping. It has no SLA, no production guarantees, runs against shared Ollama models, and rate-limits at 20 req/min · 500 req/day per tenant. Read the [Try AxonFlow — Free Trial Server](https://docs.getaxonflow.com/docs/deployment/community-saas/) page for the full disclosure, including [data retention](https://docs.getaxonflow.com/docs/deployment/community-saas/#limitations-and-disclaimers) and [registration mechanics](https://docs.getaxonflow.com/docs/deployment/community-saas/#registration).
45
+
46
+ Auto-registration credentials persist at `$AXONFLOW_CONFIG_DIR/try-registration.json` (mode `0600`).
47
+
48
+ ### Self-hosted: your own AxonFlow
49
+
50
+ Point the plugin at an AxonFlow instance you run. Nothing leaves your network except the anonymous 7-day heartbeat.
51
+
52
+ ```yaml
53
+ plugins:
54
+ axonflow-governance:
55
+ endpoint: https://axonflow.your-corp.example.com
56
+ clientId: your-client-id
57
+ clientSecret: your-secret
58
+ ```
59
+
60
+ This is the recommended setup for any real workflow, real systems, or sensitive data. See [Getting Started](https://docs.getaxonflow.com/docs/getting-started/) for deployment options or the [OpenClaw integration guide](https://docs.getaxonflow.com/docs/integration/openclaw/) for the architecture.
61
+
62
+ ### Air-gapped: zero outbound
63
+
64
+ If you need the plugin to make *no* outbound calls at all — air-gapped lab, regulated network, etc. — set both:
65
+
66
+ ```bash
67
+ export AXONFLOW_COMMUNITY_SAAS=0 # disable auto-bootstrap
68
+ export AXONFLOW_TELEMETRY=off # disable 7-day heartbeat
69
+ ```
70
+
71
+ …and configure `pluginConfig.endpoint` to a self-hosted AxonFlow on the same network. With these set, no traffic leaves your environment.
72
+
73
+ ### Environment variables (optional)
74
+
75
+ | Variable | Effect |
76
+ |---|---|
77
+ | `AXONFLOW_TELEMETRY=off` | Disables the 7-day anonymous heartbeat to `checkpoint.getaxonflow.com`. Accepted off-values: `off`, `0`, `false`, `no`. |
78
+ | `AXONFLOW_COMMUNITY_SAAS=0` | Disables auto-registration with `try.getaxonflow.com`. You must then set `pluginConfig.endpoint` for the plugin to enforce policy. Accepted off-values: `0`, `false`, `off`, `no`. |
79
+ | `AXONFLOW_CACHE_DIR` | Overrides the per-user cache dir (telemetry stamp, rate-limit backoff). Defaults to `$XDG_CACHE_HOME/axonflow` (Linux), `~/Library/Caches/axonflow` (macOS), `%LOCALAPPDATA%\axonflow` (Windows). |
80
+ | `AXONFLOW_CONFIG_DIR` | Overrides the per-user config dir (Community-SaaS registration file, disclosure stamp). Defaults to OS conventions. |
27
81
 
28
82
  ---
29
83
 
@@ -7,8 +7,7 @@
7
7
  * resulting credential to a 0600 file under the user's config dir, and
8
8
  * returns Basic-auth credentials the caller can hand to the AxonFlow client.
9
9
  *
10
- * Design rules (per feedback_telemetry_heartbeat_design_rules.md and
11
- * feedback_pg_advisory_lock_pin_connection.md):
10
+ * Design rules:
12
11
  * - Stamp-on-delivery: registration file is written ONLY after the
13
12
  * POST returns 201 with a valid response body. A network failure
14
13
  * leaves the previous (or absent) state untouched.
@@ -23,6 +22,13 @@
23
22
  * - Cross-platform cache dir resolution (Linux/macOS/Windows).
24
23
  * - Refuses to load a registration file with non-0600 permissions
25
24
  * (defends against silent credential leak via accidental chmod).
25
+ * - Operator opt-out via AXONFLOW_COMMUNITY_SAAS=0 short-circuits the
26
+ * bootstrap entirely; callers see source="opted-out".
27
+ *
28
+ * Environment + filesystem operations live in community-saas-context.ts.
29
+ * This module is the orchestration + network-only side of the bootstrap:
30
+ * it imports plain values from the context module and only issues HTTP
31
+ * requests + invokes the plugin logger.
26
32
  */
27
33
  export interface BootstrapResult {
28
34
  /** Resolved AxonFlow agent endpoint to use for subsequent requests. */
@@ -31,8 +37,35 @@ export interface BootstrapResult {
31
37
  clientId: string;
32
38
  /** Plain-text credential paired with clientId for Basic auth. */
33
39
  clientSecret: string;
34
- /** Source for telemetry / logging (`community-saas-fresh`, `community-saas-cached`, etc). */
35
- source: "fresh-registration" | "cached-registration" | "rate-limited" | "failed";
40
+ /**
41
+ * Source for telemetry / logging:
42
+ * "fresh-registration" — first-time POST to /api/v1/register succeeded.
43
+ * "cached-registration" — existing on-disk credential is fresh enough.
44
+ * "rate-limited" — 429 from the registrar; backoff active.
45
+ * "failed" — network or response error; no credential.
46
+ * "opted-out" — operator set AXONFLOW_COMMUNITY_SAAS=0.
47
+ */
48
+ source: "fresh-registration" | "cached-registration" | "rate-limited" | "failed" | "opted-out";
49
+ }
50
+ /**
51
+ * Optional injection hook for the disclosure banner. The plugin entry
52
+ * point passes its OpenClaw `PluginLogger.warn` here so the banner shows
53
+ * up in plugin/gateway logs in the same style as other plugin warnings.
54
+ * When omitted, falls back to `process.stderr.write` so test harnesses
55
+ * and ad-hoc invocations still surface the disclosure.
56
+ */
57
+ export type DisclosureLogger = (message: string) => void;
58
+ export interface BootstrapOptions {
59
+ registerUrl?: string;
60
+ endpoint?: string;
61
+ pluginVersion?: string;
62
+ fetchImpl?: typeof fetch;
63
+ now?: () => Date;
64
+ /**
65
+ * Caller-provided logger for the first-load Community-SaaS disclosure.
66
+ * Plugin entry passes `api.logger.warn`; tests pass a capture function.
67
+ */
68
+ disclosureLogger?: DisclosureLogger;
36
69
  }
37
70
  /**
38
71
  * Bootstrap a Community-SaaS registration. Returns null if bootstrap was
@@ -43,13 +76,7 @@ export interface BootstrapResult {
43
76
  * Safe to call concurrently — the second concurrent call awaits the first
44
77
  * rather than racing.
45
78
  */
46
- export declare function bootstrapCommunitySaas(opts?: {
47
- registerUrl?: string;
48
- endpoint?: string;
49
- pluginVersion?: string;
50
- fetchImpl?: typeof fetch;
51
- now?: () => Date;
52
- }): Promise<BootstrapResult | null>;
79
+ export declare function bootstrapCommunitySaas(opts?: BootstrapOptions): Promise<BootstrapResult | null>;
53
80
  /**
54
81
  * Test-only: clear the in-flight gate so test cases can exercise concurrent
55
82
  * bootstrap calls without sharing state across tests.
@@ -1 +1 @@
1
- {"version":3,"file":"community-saas-bootstrap.d.ts","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAuBH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB,6FAA6F;IAC7F,MAAM,EAAE,oBAAoB,GAAG,qBAAqB,GAAG,cAAc,GAAG,QAAQ,CAAC;CAClF;AASD;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAAC,IAAI,CAAC,EAAE;IAClD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQlC;AA8ND;;;GAGG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
1
+ {"version":3,"file":"community-saas-bootstrap.d.ts","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AA4BH,MAAM,WAAW,eAAe;IAC9B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;IACjB,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;;OAOG;IACH,MAAM,EACF,oBAAoB,GACpB,qBAAqB,GACrB,cAAc,GACd,QAAQ,GACR,WAAW,CAAC;CACjB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AASzD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC1C,IAAI,CAAC,EAAE,gBAAgB,GACtB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQjC;AA4MD;;;GAGG;AACH,wBAAgB,+BAA+B,IAAI,IAAI,CAEtD"}
@@ -7,8 +7,7 @@
7
7
  * resulting credential to a 0600 file under the user's config dir, and
8
8
  * returns Basic-auth credentials the caller can hand to the AxonFlow client.
9
9
  *
10
- * Design rules (per feedback_telemetry_heartbeat_design_rules.md and
11
- * feedback_pg_advisory_lock_pin_connection.md):
10
+ * Design rules:
12
11
  * - Stamp-on-delivery: registration file is written ONLY after the
13
12
  * POST returns 201 with a valid response body. A network failure
14
13
  * leaves the previous (or absent) state untouched.
@@ -23,11 +22,17 @@
23
22
  * - Cross-platform cache dir resolution (Linux/macOS/Windows).
24
23
  * - Refuses to load a registration file with non-0600 permissions
25
24
  * (defends against silent credential leak via accidental chmod).
25
+ * - Operator opt-out via AXONFLOW_COMMUNITY_SAAS=0 short-circuits the
26
+ * bootstrap entirely; callers see source="opted-out".
27
+ *
28
+ * Environment + filesystem operations live in community-saas-context.ts.
29
+ * This module is the orchestration + network-only side of the bootstrap:
30
+ * it imports plain values from the context module and only issues HTTP
31
+ * requests + invokes the plugin logger.
26
32
  */
27
- import * as fs from "fs";
28
- import * as os from "os";
29
33
  import * as path from "path";
30
34
  import { axonflowCacheDir, axonflowConfigDir } from "./cache-dir.js";
35
+ import { buildRegistrationLabel, disclosureStampPath, ensureSecureDir, hasShownDisclosure, isCommunitySaasOptedOut, isWithinBackoff, markDisclosureShown, readRegistrationIfFreshAndSafe, resolveHarnessInputs, unlinkIfExists, writeFileAtomicallyWithMode, } from "./community-saas-context.js";
31
36
  const REGISTER_URL_DEFAULT = "https://try.getaxonflow.com/api/v1/register";
32
37
  const ENDPOINT_DEFAULT = "https://try.getaxonflow.com";
33
38
  const REGISTRATION_FILE_NAME = "try-registration.json";
@@ -61,15 +66,18 @@ export async function bootstrapCommunitySaas(opts) {
61
66
  return inFlight;
62
67
  }
63
68
  async function bootstrapCommunitySaasInner(opts) {
64
- // Test-harness overrides only honoured when AXONFLOW_HARNESS=1 and
65
- // exclusively used by tests/heartbeat-real-stack/. Production callers
66
- // leave AXONFLOW_HARNESS unset and the URLs stay pinned to
67
- // try.getaxonflow.com.
68
- const harnessOn = process.env.AXONFLOW_HARNESS === "1";
69
- const harnessRegister = harnessOn ? process.env.AXONFLOW_HARNESS_REGISTER_URL : "";
70
- const harnessAgent = harnessOn ? process.env.AXONFLOW_HARNESS_AGENT_ENDPOINT : "";
71
- const registerUrl = opts?.registerUrl ?? (harnessRegister || REGISTER_URL_DEFAULT);
72
- const endpoint = opts?.endpoint ?? (harnessAgent || ENDPOINT_DEFAULT);
69
+ // 0. Operator opt-out short-circuits everything. No env-var disclosure
70
+ // lookup, no fs touches, no network — return immediately.
71
+ if (isCommunitySaasOptedOut()) {
72
+ return { endpoint: opts?.endpoint ?? ENDPOINT_DEFAULT, clientId: "", clientSecret: "", source: "opted-out" };
73
+ }
74
+ // 1. Test-harness URL overrides only honoured when AXONFLOW_HARNESS=1
75
+ // and exclusively used by tests/heartbeat-real-stack/. Production
76
+ // callers leave AXONFLOW_HARNESS unset and the URLs stay pinned to
77
+ // try.getaxonflow.com.
78
+ const harness = resolveHarnessInputs();
79
+ const registerUrl = opts?.registerUrl ?? (harness.harnessRegisterUrl || REGISTER_URL_DEFAULT);
80
+ const endpoint = opts?.endpoint ?? (harness.harnessAgentEndpoint || ENDPOINT_DEFAULT);
73
81
  const fetchFn = opts?.fetchImpl ?? fetch;
74
82
  const now = opts?.now ?? (() => new Date());
75
83
  const configDir = axonflowConfigDir();
@@ -81,24 +89,11 @@ async function bootstrapCommunitySaasInner(opts) {
81
89
  const backoffFile = cacheDir
82
90
  ? path.join(cacheDir, BACKOFF_FILE_NAME)
83
91
  : "";
84
- try {
85
- fs.mkdirSync(configDir, { recursive: true, mode: 0o700 });
86
- // mkdir's mode only applies to directories it creates. A user with
87
- // ~/.config already at 0755 would otherwise hold our 0600 credential
88
- // file inside a traversable directory; chmod restores the dir-mode
89
- // contract on every invocation. Skip on Windows (mode bits don't map).
90
- if (process.platform !== "win32") {
91
- try {
92
- fs.chmodSync(configDir, 0o700);
93
- }
94
- catch { /* best effort */ }
95
- }
96
- }
97
- catch {
92
+ if (!ensureSecureDir(configDir)) {
98
93
  return null;
99
94
  }
100
95
  // Fast path: existing registration is fresh enough.
101
- const cached = readRegistrationIfFreshAndSafe(registrationFile, now);
96
+ const cached = readRegistrationIfFreshAndSafe(registrationFile, now, REFRESH_WINDOW_MS);
102
97
  if (cached) {
103
98
  return {
104
99
  endpoint: cached.endpoint ?? endpoint,
@@ -111,8 +106,18 @@ async function bootstrapCommunitySaasInner(opts) {
111
106
  if (backoffFile && isWithinBackoff(backoffFile, now)) {
112
107
  return { endpoint, clientId: "", clientSecret: "", source: "rate-limited" };
113
108
  }
109
+ // First-load disclosure: announce the auto-registration once per machine
110
+ // before issuing the network call. The stamp is written after the
111
+ // banner emits so we don't re-warn on subsequent loads, but never before
112
+ // the banner emits so a crash mid-disclosure stays loud.
113
+ emitFirstLoadDisclosureIfNeeded({
114
+ configDir,
115
+ endpoint,
116
+ registerUrl,
117
+ logger: opts?.disclosureLogger,
118
+ });
114
119
  // Issue the registration.
115
- const label = buildLabel(opts?.pluginVersion);
120
+ const label = buildRegistrationLabel(opts?.pluginVersion);
116
121
  let response;
117
122
  try {
118
123
  const ctl = new AbortController();
@@ -133,15 +138,8 @@ async function bootstrapCommunitySaasInner(opts) {
133
138
  return { endpoint, clientId: "", clientSecret: "", source: "failed" };
134
139
  }
135
140
  if (response.status === 429) {
136
- if (backoffFile && cacheDir) {
141
+ if (backoffFile && cacheDir && ensureSecureDir(cacheDir)) {
137
142
  try {
138
- fs.mkdirSync(cacheDir, { recursive: true, mode: 0o700 });
139
- if (process.platform !== "win32") {
140
- try {
141
- fs.chmodSync(cacheDir, 0o700);
142
- }
143
- catch { /* best effort */ }
144
- }
145
143
  const backoffUntil = Math.floor(now().getTime() / 1000) + BACKOFF_SECONDS;
146
144
  writeFileAtomicallyWithMode(backoffFile, String(backoffUntil), 0o600);
147
145
  }
@@ -176,10 +174,7 @@ async function bootstrapCommunitySaasInner(opts) {
176
174
  try {
177
175
  writeFileAtomicallyWithMode(registrationFile, JSON.stringify(parsed), 0o600);
178
176
  if (backoffFile) {
179
- try {
180
- fs.unlinkSync(backoffFile);
181
- }
182
- catch { /* fine */ }
177
+ unlinkIfExists(backoffFile);
183
178
  }
184
179
  }
185
180
  catch {
@@ -194,83 +189,63 @@ async function bootstrapCommunitySaasInner(opts) {
194
189
  source: "fresh-registration",
195
190
  };
196
191
  }
197
- function readRegistrationIfFreshAndSafe(file, now) {
198
- let stat;
199
- try {
200
- stat = fs.statSync(file);
192
+ function emitFirstLoadDisclosureIfNeeded(inputs) {
193
+ const stampFile = disclosureStampPath(inputs.configDir);
194
+ if (hasShownDisclosure(stampFile)) {
195
+ return;
201
196
  }
202
- catch {
203
- return null;
197
+ const banner = buildDisclosureBanner(inputs.endpoint, inputs.registerUrl);
198
+ const delivered = emitDisclosureBanner(banner, inputs.logger);
199
+ if (delivered) {
200
+ markDisclosureShown(stampFile);
204
201
  }
205
- // Refuse to read a registration file with non-0600 permissions. World-
206
- // readable credential storage is a real bug; the caller surfaces the
207
- // refusal so the user knows to delete + re-register.
208
- if (process.platform !== "win32") {
209
- const mode = stat.mode & 0o777;
210
- if (mode !== 0o600) {
211
- try {
212
- process.stderr.write(`[AxonFlow] ${file} has unsafe permissions (${mode.toString(8).padStart(3, "0")}); refusing to use. ` +
213
- `Re-register: rm ${JSON.stringify(file)} and reload.\n`);
214
- }
215
- catch { /* stderr unavailable in some hosts */ }
216
- return null;
202
+ // If neither logger nor stderr accepted the banner, leave the stamp
203
+ // unwritten so the next load tries again. Better to re-warn once we have
204
+ // a working output than to silently swallow the disclosure.
205
+ }
206
+ function buildDisclosureBanner(endpoint, registerUrl) {
207
+ const url = new URL(registerUrl);
208
+ const host = url.host;
209
+ return [
210
+ "AxonFlow Governance Community SaaS auto-registration",
211
+ "",
212
+ ` This plugin will register with ${host} (Community SaaS) and use it`,
213
+ " to evaluate tool inputs and message bodies for policy + audit.",
214
+ "",
215
+ " What is sent off-host on each governed call:",
216
+ " - tool name + arguments before execution",
217
+ " - outbound message bodies before delivery",
218
+ " What is NOT sent: LLM provider keys, OpenClaw conversation history",
219
+ " outside governed tools, or any data outside the OpenClaw runtime.",
220
+ "",
221
+ " To opt out: set AXONFLOW_COMMUNITY_SAAS=0 in your environment, or",
222
+ " point the plugin at your own AxonFlow instance:",
223
+ " pluginConfig.endpoint = \"https://your-axonflow.example.com\"",
224
+ "",
225
+ " This message shows once per machine; remove the disclosure stamp",
226
+ ` to re-display: rm "$AXONFLOW_CONFIG_DIR"/openclaw-plugin-community-saas-disclosure-shown`,
227
+ ` Default endpoint: ${endpoint}`,
228
+ " Docs: https://docs.getaxonflow.com/docs/integration/openclaw/",
229
+ ].join("\n");
230
+ }
231
+ function emitDisclosureBanner(banner, logger) {
232
+ if (logger) {
233
+ try {
234
+ logger(banner);
235
+ return true;
236
+ }
237
+ catch {
238
+ // Fall through to stderr.
217
239
  }
218
240
  }
219
- let parsed;
220
- try {
221
- const raw = fs.readFileSync(file, "utf8");
222
- parsed = JSON.parse(raw);
223
- }
224
- catch {
225
- return null;
226
- }
227
- if (typeof parsed.tenant_id !== "string" || parsed.tenant_id.length === 0 ||
228
- typeof parsed.secret !== "string" || parsed.secret.length === 0 ||
229
- typeof parsed.expires_at !== "string") {
230
- return null;
231
- }
232
- const expiresMs = Date.parse(parsed.expires_at);
233
- if (!Number.isFinite(expiresMs)) {
234
- return null;
235
- }
236
- const remaining = expiresMs - now().getTime();
237
- if (remaining < REFRESH_WINDOW_MS) {
238
- return null;
239
- }
240
- return parsed;
241
- }
242
- function isWithinBackoff(backoffFile, now) {
243
241
  try {
244
- const raw = fs.readFileSync(backoffFile, "utf8").trim();
245
- const until = Number(raw);
246
- if (!Number.isFinite(until) || until <= 0)
247
- return false;
248
- return until > Math.floor(now().getTime() / 1000);
242
+ process.stderr.write(banner + "\n");
243
+ return true;
249
244
  }
250
245
  catch {
251
246
  return false;
252
247
  }
253
248
  }
254
- function buildLabel(pluginVersion) {
255
- const version = pluginVersion ?? "unknown";
256
- const platform = `${os.type()}-${os.arch()}`;
257
- const label = `openclaw-plugin@${version} / ${platform}`;
258
- return label.length > 255 ? label.slice(0, 255) : label;
259
- }
260
- function writeFileAtomicallyWithMode(file, content, mode) {
261
- // tmp file in the same directory so rename is atomic on POSIX. On Windows,
262
- // fs.renameSync replaces the destination atomically since Node 14+.
263
- const dir = path.dirname(file);
264
- const tmp = path.join(dir, `${path.basename(file)}.tmp.${process.pid}`);
265
- fs.writeFileSync(tmp, content, { mode });
266
- // chmod again because some filesystems / umask combinations ignore the
267
- // mode passed to writeFileSync for already-existing temp files.
268
- try {
269
- fs.chmodSync(tmp, mode);
270
- }
271
- catch { /* best effort */ }
272
- fs.renameSync(tmp, file);
273
- }
274
249
  /**
275
250
  * Test-only: clear the in-flight gate so test cases can exercise concurrent
276
251
  * bootstrap calls without sharing state across tests.
@@ -1 +1 @@
1
- {"version":3,"file":"community-saas-bootstrap.js","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"community-saas-bootstrap.js","sourceRoot":"","sources":["../src/community-saas-bootstrap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Community-SaaS bootstrap context — environment + filesystem operations.
3
+ *
4
+ * Mirrors the split applied to the heartbeat path: env access and fs
5
+ * reads/writes live here, away from the network-sending side
6
+ * (community-saas-bootstrap.ts). Static-analysis heuristics that flag
7
+ * env-or-fs-read co-located with outbound HTTP therefore do not trip on
8
+ * the compiled bootstrap module.
9
+ *
10
+ * Pure-data module: callers receive plain values and pass them into the
11
+ * orchestration layer. No outbound HTTP lives here.
12
+ */
13
+ /**
14
+ * Test-harness inputs read from process.env. Only honoured when
15
+ * AXONFLOW_HARNESS=1 — production callers leave the var unset and the
16
+ * defaults pin to try.getaxonflow.com.
17
+ */
18
+ export interface HarnessInputs {
19
+ harnessOn: boolean;
20
+ harnessRegisterUrl: string;
21
+ harnessAgentEndpoint: string;
22
+ }
23
+ export declare function resolveHarnessInputs(): HarnessInputs;
24
+ /**
25
+ * Operator opt-out for Community-SaaS auto-bootstrap. Honours
26
+ * AXONFLOW_COMMUNITY_SAAS = "0" | "false" | "off" | "no"
27
+ * (case-insensitive). Any other value (including unset) leaves the default
28
+ * auto-bootstrap behaviour unchanged.
29
+ *
30
+ * This is the only programmatic way an operator can disable the implicit
31
+ * try.getaxonflow.com registration without supplying a self-hosted
32
+ * endpoint. Documented in README and surfaced in the first-load disclosure
33
+ * banner so the consent surface is real.
34
+ */
35
+ export declare function isCommunitySaasOptedOut(): boolean;
36
+ /**
37
+ * Ensure a directory exists with mode 0o700 (owner-only on POSIX). Returns
38
+ * true on success or false on any failure so callers can degrade safely.
39
+ */
40
+ export declare function ensureSecureDir(dir: string): boolean;
41
+ export interface PersistedRegistration {
42
+ tenant_id: string;
43
+ secret: string;
44
+ expires_at: string;
45
+ endpoint?: string;
46
+ }
47
+ /**
48
+ * Read a Community-SaaS registration file if it is fresh enough to use,
49
+ * has well-formed contents, and (on POSIX) lives at mode 0o600. Returns
50
+ * null when the file is missing, malformed, expired, or unsafe.
51
+ *
52
+ * `refreshWindowMs` lets the caller decide how aggressively to refresh —
53
+ * passing the same window ensures cached + fresh paths agree on lifetime.
54
+ */
55
+ export declare function readRegistrationIfFreshAndSafe(file: string, now: () => Date, refreshWindowMs: number): PersistedRegistration | null;
56
+ export declare function isWithinBackoff(backoffFile: string, now: () => Date): boolean;
57
+ /**
58
+ * Atomic file write (tmp + rename) with an explicit POSIX mode. Used for
59
+ * the registration file (0o600) and the rate-limit backoff stamp (0o600).
60
+ */
61
+ export declare function writeFileAtomicallyWithMode(file: string, content: string, mode: number): void;
62
+ export declare function unlinkIfExists(file: string): void;
63
+ /**
64
+ * Build the registration label sent in the POST body. Pure stdlib — no env
65
+ * reads, no fs reads — kept here so the bootstrap module stays free of any
66
+ * `os.*` calls that might confuse future scanner heuristics.
67
+ */
68
+ export declare function buildRegistrationLabel(pluginVersion: string | undefined): string;
69
+ /**
70
+ * First-load disclosure stamp helpers. The bootstrap path emits a one-time
71
+ * warning to the plugin logger explaining that auto-Community-SaaS
72
+ * registration is about to happen and how to opt out. The stamp keeps the
73
+ * warning from re-firing on every plugin reload.
74
+ *
75
+ * Stamp file lives next to the registration file so it shares the same
76
+ * config-dir lifecycle (rm of try-registration.json without a re-warn is
77
+ * intentional; the user already knows we register).
78
+ */
79
+ export declare function disclosureStampPath(configDir: string): string;
80
+ export declare function hasShownDisclosure(stampFile: string): boolean;
81
+ export declare function markDisclosureShown(stampFile: string): void;
82
+ //# sourceMappingURL=community-saas-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"community-saas-context.d.ts","sourceRoot":"","sources":["../src/community-saas-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAQH;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,wBAAgB,oBAAoB,IAAI,aAAa,CAKpD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAKjD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAWpD;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,IAAI,EACf,eAAe,EAAE,MAAM,GACtB,qBAAqB,GAAG,IAAI,CA0C9B;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,IAAI,GAAG,OAAO,CAU7E;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAM7F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAGjD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKhF;AAED;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAQ7D;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAQ3D"}