Skip to content

fix(ai): load no remote images, and pin the on-device model - #13

Merged
b3hr4d merged 1 commit into
design/b3note-cutefrom
claude/project-thread-fp9vgu
Oct 27, 2026
Merged

b3hr4d merged 1 commit into
design/b3note-cutefrom
claude/project-thread-fp9vgu

Conversation

@b3hr4d

@b3hr4d b3hr4d commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Two fixes for #12's on-device AI, so its promise holds that no note text leaves the browser. This PR targets #12's branch, so merging it puts both into #12 before the deploy.

Changes

  • No remote images. AI answers render as Markdown, and the CSP's img-src allowed any https: host. A prompt hidden in a note (pasted from a web page, say) could steer the model into writing a Markdown image whose URL carries other notes' text, and the browser would fetch it. An image in a shared note also told its sender when it was opened.
    • frontend/src/components/Markdown.tsx: images render as links to them, never as <img>.
    • frontend/public/.ic-assets.json5: img-src 'self' data:. The app has no remote images (checked: no <img>, no CSS url() in src/).
  • The WebLLM model, pinned and hash-checked. web-llm 0.2.85's own record downloads the model's files from huggingface.co/.../resolve/main/ and its WebAssembly library from raw.githubusercontent.com/mlc-ai/binary-mlc-llm-libs/main/, with no hash check, so a push to either repository could change the code that reads decrypted notes.
    • frontend/src/lib/localAi.ts: WEBLLM_CONFIG pins the model at Hugging Face commit 9bd564b and the library at 025bcaf (the binary-mlc-llm-libs commit that added the v0_2_84 libraries; the file is unchanged since). It gives web-llm SRI hashes for mlc-chat-config.json, tokenizer.json and the library, with onFailure: "error". CreateMLCEngine and deleteModelAllInfoInCache both get it.
    • The weights are pinned by the commit, not hashed: web-llm 0.2.85 checks only the config, the tokenizer and the library.
  • README.md: the "On-device AI" bullet now covers the pin, and a new "No remote images" bullet explains the image rule.

Verification

  • Fact (run here): in frontend/, pnpm format:check, pnpm typecheck, pnpm test (9 files, 56 tests) and pnpm build pass, and src/canisters is unchanged.
  • Fact (run here): the three hashes were computed from the files downloaded at the pinned URLs. web-llm's own verifyIntegrity accepts all three, and it refuses the library with one bit changed (IntegrityError). Today's main copy of the library has the same hash as the pinned one.
  • Fact (run here): nine injected faults each failed a test, and were reverted:
    • Markdown loading images;
    • img-src allowing https:;
    • the engine or the removal built from web-llm's own list;
    • the library or the model files following main;
    • no library hash;
    • onFailure: "warn";
    • the record drifting from web-llm's.
  • New tests:
    • tests/markdown.test.tsx: no <img>, the image is a link.
    • tests/csp.test.ts: img-src is exactly 'self' data:.
    • src/lib/localAi.test.ts: the record is web-llm's own with commits and hashes, and it is the one loading and removal use.
  • Open: like Apply the B3Pay design system to B3Note and add on-device AI #12 itself, not run with a real model here (no GPU). The first real download will show whether the hashes and pinned URLs load end to end.

Notes

  • A web-llm upgrade that moves to new model libraries (modelVersion) needs a new pin. localAi.test.ts fails on that upgrade, because the record no longer matches web-llm's.

Generated by Claude Code

Two holes in the on-device AI's promise that no note text leaves the
browser.

Remote images. AI answers render as Markdown, and the CSP's img-src
allowed any https host. A prompt hidden in a note (pasted from a web
page, say) could steer the model into writing a Markdown image whose URL
carries other notes' text, and the browser would fetch it. An image in a
shared note also told its sender when it was opened. Markdown now shows
images as links, and img-src allows only 'self' and data:.

The WebLLM model. web-llm's own record downloads the model's files and
its WebAssembly library from the main branches of Hugging Face and
GitHub, unchecked. WEBLLM_CONFIG pins both at a commit (Hugging Face
9bd564b, binary-mlc-llm-libs 025bcaf, the commit that added the v0_2_84
library) and gives web-llm SRI hashes for the config, tokenizer.json and
the library, with onFailure "error". Loading and removing the model both
use it. A test compares it with web-llm 0.2.85's own record.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017cSwEz3Q2o3DpuJ8qSQzY5
@b3hr4d b3hr4d self-assigned this Oct 26, 2026
@b3hr4d
b3hr4d merged commit caf5d3e into design/b3note-cute Oct 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants