RFC-001 — Bridge-first architecture + settings/feature-flags
- Status: Draft
- Authors: @yiidtw
- Created: 2026-04-21
- Supersedes: SPEC.md § Roadmap Day 3 item “standalone mode”
- Related: RFC-002 (RSS), amem-sh
youtubepipeline
TL;DR
Reposition the Chrome extension as amem Clipper (working name, not yet branded) — a thin companion to the native amem binary, explicitly modelled on the Apple Watch → iPhone relationship. Drop standalone mode from the roadmap: the binary is a hard prerequisite. When the bridge is unreachable or a feature flag is off, the relevant UI is grayed out in place with a single-click enable/install affordance, not hidden. Heavy capture features (YouTube, RSS, Drive) are feature-flagged in ~/.amem/config.toml and lazy-loaded on demand.
Positioning and naming
The extension and the CLI are not peers. The CLI is amem; the extension is a peripheral that surfaces two capture moments (a browser tab, a YouTube video) that would otherwise require copying a URL into a terminal. Everything downstream of capture — storage, compile, transcription, MCP, recall — lives in the binary.
Working name: amem Clipper. Inherits the Evernote Web Clipper lineage (users know what “clipper” means), avoids collision with the internal “bridge” process name (amem-bridge server on WS 7600), and leaves the brand “amem” attached to the core product rather than the peripheral. The name is a placeholder — final branding lands with the CWS submission.
Product model:
| Layer | Name | Role |
|---|---|---|
| Core | amem (CLI + library + MCP) | The product. Storage, compile, recall, agent API. |
| Peripheral | amem Clipper (Chrome extension) | Capture UI for the browser. Cannot function without the core. |
| Internal | amem-bridge (the WS 7600 process) | Implementation detail — users rarely name this. |
This positioning reshapes every downstream decision:
- “Does the extension need feature X?” → Only if feature X is a capture moment. Storage, recall, etc. always live server-side.
- “What happens with no binary?” → Clipper is bricked, like an Apple Watch with no paired iPhone. Frontload install; don’t half-ship.
- “Where do settings live?” → In
~/.amem/config.tomlon disk. Clipper renders them via a bridge RPC; it never holds authoritative state.
Motivation
The original SPEC envisioned two extension modes:
| Mode | Day | Requirement |
|---|---|---|
| Bridge | 2 | Native amem binary + WS 7600 |
| Standalone | 3 | No native binary; cloud sync via Drive |
After shipping the YouTube pipeline we hit three things that make standalone look worse than we expected:
- Standalone is structurally crippled. Compile requires Ollama, transcription requires whisper-rs — neither runs in a Chrome MV3 extension. A standalone capture can store URLs but cannot compile, so the wiki never builds. Agent-side MCP is also dark because MCP is native-only.
- Feature cost is real. YouTube alone needs yt-dlp (~20 MB), ffmpeg (~60 MB), whisper model (75 MB–3 GB depending on size). Bundling all of this into default install breaks “offline-first with zero cloud dependencies by default” by shifting the pain from network to disk.
- Dual code paths are a maintenance tax. Standalone + bridge would mean two storage backends, two capture pipelines, two sets of bugs. The SPEC’s principle 4 (“complement aide, don’t duplicate”) applies to our own internals too.
Meanwhile, bridge mode is already the richer experience. A 30-second curl amem.sh/install | sh is less friction than a crippled standalone fork.
Proposal
1. Bridge is the only mode — UI degrades by graying out
Clipper on cold-start pings ws://127.0.0.1:7600/status. Based on the response, individual UI regions render as enabled, gray-disabled with a one-click enable, or gray-disabled with install CTA:
| State | UI for capture-web | UI for YouTube | UI for RSS | Global banner |
|---|---|---|---|---|
| Bridge unreachable | gray, tooltip “amem not running” | gray | gray | “Install amem → curl amem.sh/install | sh” with copy button |
| Bridge OK, all features off | enabled | gray, inline “Enable YouTube (~95 MB)” button | gray, inline “Enable RSS” button | — |
| Bridge OK, YT enabled | enabled | enabled | gray + enable | — |
| Bridge OK, all on | enabled | enabled | enabled | — |
Rationale: grayed controls are discoverable (user sees the feature exists, understands why it’s off) and honest (no hidden states). All-or-nothing install cards punish curious first-run users; per-feature gray-out is the Apple Watch “grey watch face when disconnected” pattern the positioning promises.
2. Feature flags live in ~/.amem/config.toml
Clipper renders a Settings page that maps to keys in this file via a new bridge RPC (settings_get / settings_set). The file is the single source of truth — both CLI and Clipper read/write the same keys. Clipper holds no authoritative config of its own, consistent with the peripheral positioning.
# ~/.amem/config.toml
version = 1
[features]
youtube = false # enables YT capture + compile (lazy-downloads yt-dlp + whisper model)
rss = false # enables RSS subscription ingestion (see RFC-002)
drive = false # enables Google Drive backup (Day 3)
[youtube]
whisper_model = "tiny.en" # tiny.en | base.en | small.en | medium.en
[bridge]
host = "127.0.0.1" # MUST be loopback (see Security)
port = 7600
token_file = "~/.amem/bridge.token"
3. Lazy-load on feature enable
Enabling a flag from Clipper or CLI triggers a setup routine:
amem youtube setup # CLI: downloads yt-dlp + whisper model + checks ffmpeg
amem rss setup # (RFC-002)
amem drive setup # (Day 3)
Clipper setup button → bridge RPC feature_setup({name}) → server runs the corresponding amem <name> setup, streams progress back over WS so Clipper can show a progress bar inline next to the (still grayed) control.
Graceful degradation in core flows. If a user runs amem capture <youtube-url> when features.youtube = false, the CLI prints:
YouTube capture is not enabled. To turn it on:
amem youtube setup
This will download yt-dlp (~20 MB) and the tiny.en whisper model (~75 MB).
The MCP tool amem_capture returns an analogous structured error, so agents can surface it to their user.
4. Bridge auto-start
On first install, amem install (the curl|sh script) registers a per-user background service:
- macOS:
launchctluser agent (~/Library/LaunchAgents/sh.amem.bridge.plist) - Linux:
systemctl --userunit (~/.config/systemd/user/amem-bridge.service) - Windows: Task Scheduler at-logon task (deferred; Windows support is a follow-up)
Goal: after first-run setup, the bridge is as available as Ollama is today — it just runs.
Security
Bridge-always means a persistent localhost WebSocket. Three defences, all MUST land before the “always on” posture ships:
| Defence | Mechanism |
|---|---|
| Loopback binding | Server binds 127.0.0.1, never 0.0.0.0. Reject --bind CLI flags that widen this. |
| Origin allow-list | WS handshake rejects connections whose Origin: header is not chrome-extension://<amem-clipper-prod-id> (production ID) or chrome-extension://<amem-clipper-dev-id> (dev build). |
| Token auth | On bridge start we mint a 32-byte random token to ~/.amem/bridge.token (mode 0600). Extension retrieves it via native-messaging handshake at install time. All WS messages must carry {"token": "..."} in their envelope. Tokens rotate on bridge restart. |
Threat model
| Threat | Impact | Mitigation |
|---|---|---|
| Another local program connects to WS | Could trigger amem_capture → write files to ~/.amem/raw/ | Token auth + origin check kill 99% of this |
| Disk-fill DoS via repeated capture | Fill user’s disk | Rate-limit captures per minute; refuse when ~/.amem/ exceeds configurable quota |
| Malicious browser extension connects as us | Impersonates our extension_id | Chrome refuses to forge Origin: for a different extension_id |
| RCE via yt-dlp / ffmpeg CVE | Arbitrary code execution | Use pinned versions, track security advisories; same posture as Ollama |
| Prompt injection in captured content | Poisons MCP amem_recall output | Same risk as today’s arxiv/PDF pipeline; not new from bridge-always |
Net risk: slightly higher than CLI-only (persistent WS endpoint exists), lower than an HTTP server accepting remote connections. Comparable to VS Code’s language-server loopback.
Migration
- SPEC.md §Roadmap: strike Day 3 “standalone mode”; add “bridge security hardening + install polish” and “Drive backup” (Drive stays).
- amem-clipper (repo renamed from
amem-extension2026-04-21): delete any standalone-only code paths (none should exist yet — Day 2 skeleton is bridge-only; this is a no-op today). Rename the product surface to “amem Clipper” in README, store listing, manifestname, and UI chrome. - docs:
guide/extension.mdrenamed toguide/clipper.md; its “Standalone vs Bridge” section is being rewritten as “How install works”. - README (amem-hq): clarify install-first story on all public pages, label the repo as “amem Clipper (Chrome MV3 extension)”.
Rejected alternatives
- “Pure cloud standalone” — extension + Drive only, no native binary. Breaks offline-first and agent-MCP. Also introduces OAuth complexity earlier than Day 3.
- Bundle everything in the default install. Ships ~3 GB of whisper models most users never use. Opposite of the lazy-load principle.
- Run whisper.wasm in the browser. Early 2026 performance is still 3–10× slower than native for
base.en; model download in the extension also hits MV3 storage limits.
Concrete work
See GitHub issues linked from this RFC.
amem youtube setupsubcommand + graceful-degradation prompt incapture(amem-sh)- Bridge: loopback binding +
Origin:check + token auth (amem-sh) - Bridge: auto-start service installers (macOS launchd, Linux systemd) (
amem-sh) - Clipper: bridge status probe, per-region gray-out UI, Settings page backed by
config.tomlvia bridge RPC (amem-clipper) - Clipper: rename product surface to “amem Clipper” (manifest, store listing copy, in-UI strings) — repo slug already renamed to
amem-clipper2026-04-21 (amem-clipper) - SPEC.md + docs: drop standalone, document bridge-first (
amem-hq)
Open questions
- Do we treat Ollama as a similarly lazy-loaded feature? Arguably yes — PDF compile also blocks without it. Worth a follow-up RFC if so.
- Drive backup (Day 3): should it require Pro/auth once shipped, or stay free? Product decision, not in scope here.