NotASithLord / NotASithLord/peerd
dweb: installed dwapp code is mutable with no re-verification — add an explicit fork state + grant rotation
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 410
- Forks
- 45
- Avg merge
- 11d 5h
- Merged PRs (30d)
- 1
Description
Problem
A dwapp's dweb.version_id is a provenance claim, not an integrity binding. Three concrete consequences, all verified in source:
- Writes don't check provenance.
app_write_file/app_update/edit_fileroute toappClient.writeFile/update(background/app-client.js), which resolve an appId and write straight into OPFSpeerd-apps/<appId>/with no check ofrecord.dweb,record.source, or the manifest hash. The agent (or the user via the app tab's Edit toggle) can silently rewrite code installed from a peer. - Nothing re-verifies at run time.
engine-tabs/app-tab/app-tab.jscomposes whatever is in OPFS; there is no comparison againstdweb.version_id. - Grants survive local mutation. The bridge grant key is derived from the stored
dweb.hash(peerd-distributed/apps/bridge.js,appKey), which editing doesn't update — so a locally modified dwapp keeps the room grants the user approved for the original publisher's bytes.
Additionally, the update path (background/routes/dweb.js app-update, deliberately replace-not-merge) will silently clobber local modifications with no conflict detection.
Design intent: fork state, not immutability
Local mutability is the product thesis — "your agent modifies your software" — so the fix is not Sandstorm/Cloudflare-OS-style immutable packages. It's making the fork honest:
- Fork transition. On first write to an app whose record carries
dwebwith a non-nullhash: mark the record forked (e.g.dweb.forked: true), preserving lineage (basepublisher,dwapp_id,version_id) for display as "modified from <publisher>". Registry change lands inpeerd-engine/app-registry.js(note:dwebis currently excluded fromapplyPatchby design — the fork flag needs a sanctioned mutation path, not a loosening of that rule). - Grant rotation on fork. The bridge grant key becomes fork-scoped (e.g. recompute from current content, or
fork:<appId>), so the nextjoinre-consents under the new bytes. This closes the "approved bytes ≠ running bytes" gap without blocking the edit itself. - UI surface. "Locally modified" badge in the app tab; shown in the library/catalog if relevant.
- Update conflict handling.
dweb/base/update-appon a forked app must not silently overwrite — require an explicit confirm ("discard local changes and update to the publisher's vN?") or decline. Old-version seed data is already retained (offscreen/dweb-base.jskeeps prior bundles as "the substrate for a future revert/changelog") — this is the first consumer. - Reshare already does the right thing — republishing a modified app goes out under your publisher DID and slug (
dwapp_id = H(publisher‖slug),peerd-distributed/apps/meta.js), so attribution can't be spoofed. Optional follow-up: carry base-lineage in the reshared card. - Optional follow-up: verify-on-launch. For non-forked dweb apps, recompute the bundle hash at app-tab load and compare with
version_id— catches tampering via any code path that didn't take the sanctioned fork transition. Needs a perf look (bundles are ≤50MB; hash lazily/cache by OPFS mtime).
Tests
- Bun: registry fork-transition semantics; grant-key derivation before/after fork.
- In-browser/e2e: editing a dweb-installed app forces re-consent on the next room join; update-on-forked-app prompts instead of clobbering.
Prior art
Cloudflare OS keeps app code as an immutable content-addressed snapshot (Blueprints) and puts all mutability in per-instance data; Sandstorm likewise (spk packages vs. grain storage). peerd deliberately inverts this — which is exactly why the fork boundary has to be explicit rather than implicit.
Related: #235 (dwapp actors epic — actors driving dwapps raises the stakes on knowing which bytes are running).
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with peerd-engine/app-registry.js and background/app-client.js to trace sanctioned writes, then inspect peerd-distributed/apps/bridge.js and background/routes/dweb.js for grant derivation and update behavior. Check engine-tabs/app-tab/app-tab.js and the listed Bun and browser/e2e tests. Done means fork state and lineage are recorded, forked apps require new consent and cannot be silently overwritten, and the modified state is surfaced in the UI.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- bun, javascript
- Domain
- frontend, security, testing
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100