Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 10 additions & 10 deletions community/derived.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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",
Expand Down
8 changes: 4 additions & 4 deletions scripts/lib/derive.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -65,23 +65,23 @@ 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.",
};
}

if (compatibility.state !== "current") {
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.",
};
}

if (!networkPermission) {
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.",
};
}

Expand All @@ -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.",
};
}

Expand Down
2 changes: 1 addition & 1 deletion scripts/test-derive.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
}
Expand Down
Loading