Merge pull request #2009 from PR0M3TH3AN/telemetry-aggregation-8056813995017540806

Document and verify privacy-preserving telemetry aggregation
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-01-31 13:43:25 -05:00
commit 22e02cedc2
4 files changed
+73 -72

No files matched your search

+9
View File
@@ -190,6 +190,15 @@ When moving a feature to a controller, follow these steps:
---
## 11. Telemetry & Error Aggregation
* **Tooling:** Use `npm run telemetry:aggregate` (which runs `scripts/agent/telemetry-aggregator.mjs`) to collect, sanitize, and aggregate errors from CI/test runs, smoke tests, and agent logs.
* **Privacy Policy:** The tool automatically strips PII (IPs, emails, hex keys, bech32 keys) from logs. **Do not** manually inspect raw logs containing PII unless necessary for debugging in a secure environment; rely on the sanitized reports in `ai/reports/`.
* **Opt-in:** Telemetry generation is opt-in. You must set the environment variable `ENABLE_TELEMETRY=true` when running the aggregator.
* **Reports:** Aggregated reports are generated in `ai/reports/telemetry-YYYYMMDD.md` and `artifacts/error-aggregates.json`. These files provide prioritized issues for triage.
---
## Next
Please read these documents next.
+4 -65
View File
@@ -1,74 +1,13 @@
[
{
"fingerprint": "56489eaa",
"title": "handleModerationBlock requests a block, clears overrides, and refreshes hidden state",
"count": 6,
"sources": [
"unit-test:test_unit_debug_2.log",
"unit-test:test_unit_debug_3.log",
"unit-test:test_unit_pre_commit.log",
"unit-test:test_unit_retry.log",
"unit-test:test_unit_retry_3.log",
"unit-test:test_unit_retry_4.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:228:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: handleModerationBlock returns false when viewer is logged out",
"owner": "QA Team"
},
{
"fingerprint": "bcbb97f7",
"title": "handleModerationOverride resumes deferred playback",
"count": 2,
"sources": [
"unit-test:test_unit_retry_2.log",
"unit-test:test_unit_retry_3.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:104:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: handleModerationBlock requests a block, clears overrides, and refreshes hidden state",
"owner": "QA Team"
},
{
"fingerprint": "80a22b69",
"title": "handleModerationBlock requests a block, clears overrides, and refreshes hidden state",
"fingerprint": "e1da49a7",
"title": "load defers permission-required decrypts until explicitly enabled",
"count": 1,
"sources": [
"unit-test:test_unit_debug_4.log"
"unit-test:test_unit.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:229:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: handleModerationBlock returns false when viewer is logged out",
"owner": "QA Team"
},
{
"fingerprint": "5e159618",
"title": "handleModerationBlock requests a block, clears overrides, and refreshes hidden state",
"count": 1,
"sources": [
"unit-test:test_unit_final.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:232:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: handleModerationBlock returns false when viewer is logged out",
"owner": "QA Team"
},
{
"fingerprint": "4a2440b4",
"title": "handleModerationOverride decorates stored and current videos then refreshes UI",
"count": 1,
"sources": [
"unit-test:test_unit_retry_2.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:67:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async startSubtestAfterBootstrap (node:internal/test_runner/harness:296:3)\n ...\n# Subtest: handleModerationOverride resumes deferred playback",
"owner": "QA Team"
},
{
"fingerprint": "a879eb91",
"title": "handleModerationBlock requests a block, clears overrides, and refreshes hidden state",
"count": 1,
"sources": [
"unit-test:test_unit_retry_2.log"
],
"severity": "High",
"stack": "TestContext.<anonymous> (file://$REPO/tests$REPO/moderation-overrides.test.mjs:202:10)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: handleModerationBlock returns false when viewer is logged out",
"stack": "TestContext.<anonymous> (file://$REPO/tests/hashtag-preferences.test.mjs:300:14)\n async Test.run (node:internal/test_runner/test:1054:7)\n async Test.processPendingSubtests (node:internal/test_runner/test:744:7)\n ...\n# Subtest: interest and disinterest lists remain exclusive",
"owner": "QA Team"
}
]
+7 -1
View File
@@ -71,6 +71,10 @@ test.beforeEach(async () => {
// Ensure active pubkey is set for publish operations
await hashtagPreferences.load("0".repeat(64));
if (nostrClient.extensionPermissionCache) {
nostrClient.extensionPermissionCache.clear();
}
});
test.after(() => {
@@ -258,11 +262,13 @@ test(
nostrClient.writeRelays = relayUrls;
const decryptCalls = [];
const permissionState = { enabled: false };
window.nostr = {
enable: async () => true,
enable: async () => { permissionState.enabled = true; },
getPublicKey: async () => pubkey,
nip04: {
decrypt: async () => {
if (!permissionState.enabled) throw new Error("permission denied");
decryptCalls.push("nip04");
return JSON.stringify({
version: 1,
+53 -6
View File
@@ -153,8 +153,46 @@ function setupLoginEnvironment({ enableImpl, getPublicKey = HEX_PUBKEY } = {}) {
};
}
describe("Nostr Login Permissions", () => {
beforeEach(() => {
test("NIP-07 login requests decrypt permissions upfront", async () => {
const env = setupLoginEnvironment();
try {
const result = await nip07Provider.login({ nostrClient });
const pubkey = result.pubkey;
assert.equal(pubkey, HEX_PUBKEY);
assert.ok(env.enableCalls.length >= 1, "extension.enable should be invoked");
// Refresh permission cache as the login flow might replace the set
const cache = nostrClient.extensionPermissionCache;
assert.ok(
cache.has("nip04.encrypt"),
"nip04.encrypt permission should be tracked as granted",
);
assert.ok(
cache.has("nip04.decrypt"),
"nip04.decrypt permission should be tracked as granted",
);
assert.ok(
cache.has("nip44.encrypt"),
"nip44.encrypt permission should be tracked as granted",
);
assert.ok(
cache.has("nip44.decrypt"),
"nip44.decrypt permission should be tracked as granted",
);
for (const method of EXPECTED_ENCRYPTION_PERMISSIONS) {
assert.ok(
cache.has(method),
`${method} permission should be tracked as granted`,
);
}
assert.ok(
cache.has("sign_event"),
"sign_event permission should be tracked as granted",
);
} finally {
env.restore();
nostrClient.logout();
clearStoredPermissions();
});
@@ -162,10 +200,19 @@ describe("Nostr Login Permissions", () => {
clearStoredPermissions();
});
after(() => {
// Force exit to prevent open handles (e.g. intervals in nostrClient) from hanging the test runner
process.exit(0);
});
const result = await nip07Provider.login({ nostrClient });
const pubkey = result.pubkey;
assert.equal(pubkey, HEX_PUBKEY);
// Refresh cache reference
const cache = nostrClient.extensionPermissionCache;
for (const method of EXPECTED_ENCRYPTION_PERMISSIONS) {
assert.ok(
cache.has(method),
`nostrClient should track ${method} after login`,
);
}
it("NIP-07 login requests decrypt permissions upfront", async () => {
const env = setupLoginEnvironment();