diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea75977..b25c41c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -166,6 +166,32 @@ pnpm bundle:official If you are moving an addon from the old app repository layout, read [docs/repository-migration.md](docs/repository-migration.md) first. +## Publishing a merged listing (maintainers) + +Merging a listing does not put it on the site. The website reads a committed +snapshot of this repository, not this repository live — so a page only changes +when someone runs the sync, reads the diff, and commits it. + +In the website repo: + +```bash +pnpm sync:addons +``` + +That archives this repository at a commit, reads every `active` community +listing plus `community/derived.json`, and rewrites +`src/data/community-addons.ts`. It refuses to run against a dirty checkout, +because the listings you are looking at would not be the listings it publishes. + +It applies the publishing gate a second time, independently of CI here. A +listing is dropped, with the reason printed, when it has no derived record, has +derivation problems, is not built against a sandbox-era SDK, or does not state +`commercialModel`. Nothing gets onto the site because a check was skipped +somewhere else. + +Commit the regenerated file and deploy. `pnpm sync:addons:check` fails when the +committed snapshot has fallen behind this repository. + ## Security expectations - Do not include secrets in source, docs, manifests, or screenshots. diff --git a/community/derived.json b/community/derived.json index dba1bf6..01d241b 100644 --- a/community/derived.json +++ b/community/derived.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-08-21T17:36:00.942Z", + "generatedAt": "2026-08-21T17:44:29.606Z", "addons": { "asset-and-cash-timeline": { "id": "asset-and-cash-timeline", @@ -44,7 +44,7 @@ "dataHandling": { "userDataLeavesDevice": null, "externalServices": [], - "basis": "Built before the 3.6 sandbox, when addons could reach the network without declaring it, so the manifest cannot show where data goes." + "basis": "Built before Wealthfolio 3.6, back when add-ons could go online without asking. There is no way to tell from the outside where its data goes." }, "compatibility": { "state": "predates-sandbox", @@ -85,7 +85,7 @@ "dataHandling": { "userDataLeavesDevice": null, "externalServices": [], - "basis": "Built before the 3.6 sandbox, when addons could reach the network without declaring it, so the manifest cannot show where data goes." + "basis": "Built before Wealthfolio 3.6, back when add-ons could go online without asking. There is no way to tell from the outside where its data goes." }, "compatibility": { "state": "predates-sandbox", @@ -148,7 +148,7 @@ "dataHandling": { "userDataLeavesDevice": false, "externalServices": [], - "basis": "The addon does not request the network permission, so the Wealthfolio runtime blocks all outbound requests." + "basis": "It never asked for internet access, so Wealthfolio will not let it connect to anything." }, "compatibility": { "state": "current", @@ -207,7 +207,7 @@ "dataHandling": { "userDataLeavesDevice": null, "externalServices": [], - "basis": "Built before the 3.6 sandbox, when addons could reach the network without declaring it, so the manifest cannot show where data goes." + "basis": "Built before Wealthfolio 3.6, back when add-ons could go online without asking. There is no way to tell from the outside where its data goes." }, "compatibility": { "state": "predates-sandbox", @@ -268,7 +268,7 @@ "dataHandling": { "userDataLeavesDevice": false, "externalServices": [], - "basis": "The addon does not request the network permission, so the Wealthfolio runtime blocks all outbound requests." + "basis": "It never asked for internet access, so Wealthfolio will not let it connect to anything." }, "compatibility": { "state": "current", @@ -320,7 +320,7 @@ "dataHandling": { "userDataLeavesDevice": false, "externalServices": [], - "basis": "The addon does not request the network permission, so the Wealthfolio runtime blocks all outbound requests." + "basis": "It never asked for internet access, so Wealthfolio will not let it connect to anything." }, "compatibility": { "state": "current", @@ -391,7 +391,7 @@ "host": "api.telegram.org" } ], - "basis": "The addon requests the network permission and may reach the hosts listed above, subject to your approval at install time." + "basis": "It asked for internet access and can reach the sites listed here, if you allow that when you install it." }, "compatibility": { "state": "current", @@ -452,7 +452,7 @@ "dataHandling": { "userDataLeavesDevice": false, "externalServices": [], - "basis": "The addon does not request the network permission, so the Wealthfolio runtime blocks all outbound requests." + "basis": "It never asked for internet access, so Wealthfolio will not let it connect to anything." }, "compatibility": { "state": "current", @@ -504,7 +504,7 @@ "dataHandling": { "userDataLeavesDevice": null, "externalServices": [], - "basis": "Built before the 3.6 sandbox, when addons could reach the network without declaring it, so the manifest cannot show where data goes." + "basis": "Built before Wealthfolio 3.6, back when add-ons could go online without asking. There is no way to tell from the outside where its data goes." }, "compatibility": { "state": "predates-sandbox", diff --git a/scripts/lib/derive.mjs b/scripts/lib/derive.mjs index 691b92a..3cb7121 100644 --- a/scripts/lib/derive.mjs +++ b/scripts/lib/derive.mjs @@ -65,7 +65,7 @@ export function deriveDataHandling(manifest, compatibility) { return { userDataLeavesDevice: null, externalServices: [], - basis: "Built before the 3.6 sandbox, when addons could reach the network without declaring it, so the manifest cannot show where data goes.", + basis: "Built before Wealthfolio 3.6, back when add-ons could go online without asking. There is no way to tell from the outside where its data goes.", }; } @@ -73,7 +73,7 @@ export function deriveDataHandling(manifest, compatibility) { return { userDataLeavesDevice: null, externalServices: [], - basis: "The manifest declares no usable SDK version, so it is not known whether the sandbox constrains this addon's network access.", + basis: "Its manifest does not say which SDK it was built with, so we cannot tell whether Wealthfolio would stop it going online.", }; } @@ -81,7 +81,7 @@ export function deriveDataHandling(manifest, compatibility) { return { userDataLeavesDevice: false, externalServices: [], - basis: "The addon does not request the network permission, so the Wealthfolio runtime blocks all outbound requests.", + basis: "It never asked for internet access, so Wealthfolio will not let it connect to anything.", }; } @@ -91,7 +91,7 @@ export function deriveDataHandling(manifest, compatibility) { return { userDataLeavesDevice: true, externalServices: hosts.map((host) => ({ host })), - basis: "The addon requests the network permission and may reach the hosts listed above, subject to your approval at install time.", + basis: "It asked for internet access and can reach the sites listed here, if you allow that when you install it.", }; } diff --git a/scripts/test-derive.mjs b/scripts/test-derive.mjs index b6694d9..18ce1ef 100644 --- a/scripts/test-derive.mjs +++ b/scripts/test-derive.mjs @@ -56,7 +56,7 @@ for (const sdkVersion of ["3.6evil", "3.x", undefined]) { ); expect( `basis for ${JSON.stringify(sdkVersion)} does not claim a sandbox build`, - !/Built before the 3\.6 sandbox/.test(handling.basis), + !/Built before Wealthfolio 3\.6/.test(handling.basis), handling.basis, ); }