StickySites — Project Briefing

Version 1.10.0 · Chrome MV3 · Vanilla JS · No build step

This document is a self-contained briefing for multiple audiences. Each section header marks its primary audience. Plain language is used in overview sections; precise technical terminology in developer- and reviewer-facing sections. All facts — commands, paths, versions, code references — are derived from actual repo files.


1. Executive Summary (leadership)

StickySites is a Chrome extension (Manifest V3) that puts sticky notes on every website. A floating, draggable icon cluster opens a moveable, resizable workspace panel with a rich-text editor; notes are scoped six ways — one global note, per-site, per-page, per-day, a per-site to-do list, and a per-site outliner. Everything is stored locally in the browser, with opt-in AES-256-GCM encryption at rest.

It is a local, zero-dependency tool. The packaged extension ships no third-party code and makes no external network calls — no telemetry, no accounts, no servers. All note content lives in chrome.storage.local on the user’s machine. Built by Mitchell Hudson, it runs on vanilla JavaScript with no build step: Chrome reads the source directly, so a patch bump in manifest.json is the release.

Despite its small surface, it is maintained to a real engineering standard: 72 unit tests across three suites, a CI workflow, a complete documentation suite with a CI-validated file index, and a documented security model for the encryption feature.


2. Key Features (all)


3. Problems Solved (leadership + team)

Problem What the extension does
Notes scattered across apps — context for a site lives in a separate notes app you have to switch to Notes live on the page itself, scoped to the site, page, or day you’re looking at
One-size note scope — a single notepad can’t separate “this page” from “this site” from “today” Six distinct scopes, each with its own storage key and resolver
Losing notes on SPA navigation — single-page apps change the URL without a reload The panel snapshots the active key at open and re-keys safely on popstate/hashchange/href-poll
Sensitive notes in plaintext Opt-in AES-256-GCM encryption with a PBKDF2-derived key and an in-page lock
Hotkeys hijacking the page Bare-key shortcuts were removed; note-type shortcuts now require Ctrl/Cmd (F1F6) so they never break page typing or select-all
Finding a note later A popup dashboard with full-text search, sort, type/tag filters, and Markdown export
Capturing text while reading Right-click context-menu clipping into any note type
Privacy concerns with note tools Local-only storage, zero external calls, no telemetry, no third-party scripts

4. Scope of Work (leadership + reviewers)

Built by Mitchell Hudson as an independent Chrome extension. Vanilla JavaScript, no build step, MIT-licensed. Line counts are raw wc -l at v1.10.0 (scope indicators, not SLOC).

Component Path Approx. lines
Workspace panel + rich-text editor src/content/panel.js 1,848
Outliner UI src/content/outline.js 800
Storage CRUD (shared module) src/shared/notes-storage.js 363
Other content scripts (cluster, mentions, todo, outline-ops, crypto-content, note-types, prefs, orchestrator) src/content/* ~1,274
Service worker src/background/service-worker.js 86
Crypto primitives (shared module) src/shared/crypto.js 71
Popup dashboard popup.js 869
Popout window popout.js 41
Unit tests (3 suites) tests/*.js 572

Engineering quality markers:


5. Security Posture (reviewers + leadership)

Summary for reviewers: Minimal permissions, no broad host grants, zero external calls, no telemetry. Notes can be encrypted at rest with AES-256-GCM. The one nuance worth knowing: once unlocked, the derived key is cached in chrome.storage.local and persists on disk until the user locks — convenience over a session-only posture.

Permissions

The manifest requests only storage, activeTab, and contextMenus. There are no host_permissions and no tabs, webRequest, cookies, history, or identity permissions. Content scripts do match <all_urls> — that breadth is inherent to “sticky notes on every site” — but the extension never reads or transmits page content off-device.

Encryption at rest (opt-in)

Other controls

What it does not defend against

Full threat model, permissions audit, and key lifecycle: docs/SECURITY.md.


6. Architecture Overview (reviewers + team)

A Manifest V3 extension with a deliberate module-system split.

Browser action popup (popup.*) ─┐
Popout window (popout.*) ────────┤  chrome.runtime.sendMessage

                        service worker (ES module)  ── src/background/service-worker.js
                         - context menus (6 types)     - Alt+S toggle command
                         - popout window creation       - storage.session.setAccessLevel

        chrome.runtime.sendMessage │  (STICKYSITES_TOGGLE / OPEN / CLIP / POPOUT)

content scripts (classic, ordered) ── src/content/*  → window.StickySites.*
  crypto-content → note-types → prefs → cluster → todo → outline-ops →
  outline → mentions → panel → sticky-inject (orchestrator)


        chrome.storage.local  (8 versioned keys + cached key)
        ↕ storage.onChanged → cross-tab live sync

Module-system split (the central rule)

Storage

chrome.storage.local holds eight versioned keys — stickysites_global_v1, _sites_v1, _pages_v1, _todos_v1, _outlines_v1, _daily_v1, _prefs_v1, _crypto_v1 — plus stickysites_cached_key. Records use a canonical key + label schema with fallbacks to legacy field names written before v1.10. chrome.storage.session is used only for the service worker’s startup setAccessLevel call.

Message contract

Type Direction Purpose
STICKYSITES_TOGGLE SW → content Toggle cluster visibility
STICKYSITES_OPEN SW → content Open a specific note type (optional key targets an outline doc)
STICKYSITES_CLIP SW → content Clip selected text into a note
STICKYSITES_POPOUT content → SW Open the current note in a standalone window

Full diagrams and design decisions: docs/ARCHITECTURE.md.


7. Installation & Quick Start (new users)

Install (load unpacked)

  1. Clone this repository.
  2. Open chrome://extensions and enable Developer mode.
  3. Click Load unpacked and select the repo root.
  4. Click the StickySites toolbar icon or press Alt+S on any page.

There is no build step — Chrome reads the source directly.

Development

npm install          # dev dependencies (Vitest, canvas)
npm test             # run unit tests (single pass)
npm run test:watch   # watch mode
npm run docs:check   # validate docs/high_signal_file_index.json

Requires Node ≥ 22 for the test harness. Tests run in a node environment and mock chrome.* APIs. Full setup and troubleshooting: docs/INSTALLATION.md and docs/DEVELOPMENT.md.


8. Usage Guide (team + new users)

Everyday use

Encryption

Open the popup Settings panel and enable encryption with a passphrase. All six note types are re-encrypted in place. Use Lock Now to clear the cached key; you’ll re-enter the passphrase to unlock. (See §5 for the key-persistence nuance.)


9. Dependencies (reviewers)

Runtime — none

The shipped extension has zero runtime dependencies. It runs entirely on built-in browser APIs: chrome.storage, chrome.runtime, chrome.tabs, chrome.contextMenus, chrome.commands, chrome.windows, and the Web Crypto API (crypto.subtle).

Development (devDependencies in package.json)

Package Version Rationale
vitest ^4.1.0 Unit-test runner (node environment; chrome.* mocked)
canvas ^3.2.3 Used by scripts/generate-icons.js to render the extension icons

10. Contribution & Workflow (team)

Workflow log

The repo keeps a committed operator log: append the user request to prompts.md and update the latest memory.md entry as work progresses. (A separate, gitignored .remember/ directory is used by local tooling; memory.md/prompts.md are the shareable record.)

Version bump

manifest.json.version is canonical and is the release. Bump the patch there and keep package.json.version in sync on any meaningful change.

Where things go

Validation gate

npm test && npm run docs:check

Both run in CI. Conventions: vanilla JS only (no frameworks/transpilers/bundlers); injected DOM uses the stickysites- prefix; storage keys are _v1-versioned; auto-save debounced at 500 ms.


11. Known Limitations (all)

See docs/known-issues.md for full detail.

Limitation Impact
No Shadow DOM encapsulation Injected UI shares the host DOM; aggressive host-page CSS can break the StickySites UI, and host-page JS can read the open panel. A Shadow DOM root is the planned fix.
Content-script re-injection on update Chrome may re-inject after an extension update; a guard prevents double UI, but listeners from the previous injection can be orphaned until the page is reloaded.
PBKDF2 is slow on low-end devices 600 K iterations can take 2–4 s to unlock, with no progress indicator, so the unlock button appears to hang. By design (security floor); a spinner is the planned improvement.
Cached key persists on disk until locked The derived key lives in chrome.storage.local until Lock Now or disable — not cleared on browser close (see §5).
Rich-text editor uses document.execCommand The toolbar relies on the deprecated execCommand API; it works in current Chrome but is not future-proof.

Resource Path
Architecture docs/ARCHITECTURE.md
Components docs/COMPONENTS.md
Security model docs/SECURITY.md
Development docs/DEVELOPMENT.md
Installation docs/INSTALLATION.md
Testing docs/TESTING.md
Logging docs/logging.md
Caching & optimization docs/caching-and-optimization.md
External calls (none) docs/external-calls.md
Known issues docs/known-issues.md
Codebase overview docs/codebase-overview.md
File index (machine-readable) docs/high_signal_file_index.json
Runbooks docs/runbooks/
Bootstrap audit (2026-06-17) docs/repo-bootstrap-audit-2026-06-17.md

Last updated: 2026-06-17 · v1.10.0