diff --git a/.changeset/diagnostic-access-consent.md b/.changeset/diagnostic-access-consent.md
new file mode 100644
index 000000000..9e01e5035
--- /dev/null
+++ b/.changeset/diagnostic-access-consent.md
@@ -0,0 +1,5 @@
+---
+"ftw": patch
+---
+
+The Access page states that technical diagnostics include source, logs and full backups, which can contain saved credentials, before the owner grants access.
diff --git a/web/index.html b/web/index.html
index aba81f627..0b459f479 100644
--- a/web/index.html
+++ b/web/index.html
@@ -1004,7 +1004,7 @@
Price bars (top of the chart)
-
+
diff --git a/web/settings/tabs/access.js b/web/settings/tabs/access.js
index d4738d5a9..1b0fa458e 100644
--- a/web/settings/tabs/access.js
+++ b/web/settings/tabs/access.js
@@ -33,7 +33,7 @@
"ftw.events.read": "See alerts and events",
"ftw.logs.read": "Read the box's logs, with names and secrets taken out",
"ftw.config.read": "Read the settings, with names and secrets taken out",
- "ftw.diag.run": "Run checks that change nothing",
+ "ftw.diag.run": "Read full technical diagnostics, source, logs and backups; backups can contain saved credentials",
"ftw.dispatch.hold": "Act at once, for a few minutes at a time, within these limits",
"ftw.goals.request": "Ask for lasting changes, each waiting for your yes here",
};
diff --git a/web/settings/tabs/access.test.mjs b/web/settings/tabs/access.test.mjs
index 64027e71d..b9e15d372 100644
--- a/web/settings/tabs/access.test.mjs
+++ b/web/settings/tabs/access.test.mjs
@@ -61,6 +61,13 @@ describe("the page's words follow the registry", () => {
for (const scope of Object.keys(P.SCOPE_WORDS)) assert.ok(agentScopes.includes(scope), scope);
});
+ it("warns that diagnostic access reads full backups that can contain saved credentials", () => {
+ const words = P.SCOPE_WORDS["ftw.diag.run"];
+ assert.match(words, /full technical diagnostics, source, logs and backups/);
+ assert.match(words, /backups can contain saved credentials/);
+ assert.doesNotMatch(words, /checks that change nothing/);
+ });
+
it("has words for every op an assistant may name", () => {
assert.ok(agentOps.length >= 5);
for (const op of agentOps) assert.ok(P.OP_WORDS[op], op);