Skip to content

Add content suggestion service for bookmark metadata extraction#30

Merged
pheuberger merged 2 commits into
mainfrom
claude/content-suggestion-service-u2uNI
Feb 5, 2026
Merged

Add content suggestion service for bookmark metadata extraction#30
pheuberger merged 2 commits into
mainfrom
claude/content-suggestion-service-u2uNI

Conversation

@pheuberger
Copy link
Copy Markdown
Owner

Summary

This PR adds an optional content suggestion service that automatically extracts and suggests metadata (title, description, tags, favicon) when users add bookmarks. The feature is opt-in with privacy-first design: it's stateless, sends only the URL, and users can self-host or disable entirely.

Key Changes

Backend (Signaling Server)

  • Extended server.js to serve both WebSocket signaling and HTTP APIs on the same port
  • Added /api/suggest endpoint for metadata extraction
  • Added /api/health endpoint for service health checks
  • New metadata.js module that extracts page metadata using only Node.js built-in APIs:
    • Fetches HTML with safety limits (10s timeout, 2MB max)
    • Extracts title (prefers og:title, falls back to <title>)
    • Extracts description (og:description, meta description, twitter:description)
    • Extracts tags from keywords, article:tag, article:section, og:type, and URL path patterns
    • Extracts favicon URL with fallback to /favicon.ico

Frontend (React)

  • New useContentSuggestion hook for managing suggestion state and requests
  • New content-suggestion.js service with:
    • Storage-based configuration for suggestion service URL and enabled state
    • Automatic derivation of suggestion URL from signaling server URL
    • fetchSuggestions() and testSuggestionService() functions
  • New ServiceConfigView component for configuring service URLs with privacy disclosure
  • Updated BookmarkForm to show "Suggest" button and apply suggestions to empty fields
  • Updated InboxItem to show "Suggest" button in focus mode with loading state
  • Updated SettingsView to include service configuration section

Configuration

  • Added .env.example entries for VITE_SUGGESTION_URL (optional)
  • Updated Docker build to include metadata.js

Implementation Details

  • Privacy-first: Feature is opt-in with explicit disclosure before enabling. Service is stateless and doesn't log requests.
  • No external dependencies: Metadata extraction uses only Node.js built-in APIs (no jsdom, cheerio, etc.)
  • Graceful degradation: If suggestion service is unavailable, users can still add bookmarks manually
  • Smart defaults: Suggestion URL automatically derives from signaling server URL if not explicitly configured
  • User control: Users can self-host the service, disable it, or configure a custom endpoint
  • Abort handling: In-flight requests are cancelled when component unmounts or new requests are made

https://claude.ai/code/session_01EFjPe8FV9ChZvAsgUrZ8rY

Add an opt-in content suggestion service that extracts page metadata
(title, description, tags, favicon) from bookmark URLs. The service
runs alongside the existing WebSocket signaling server on the same
Fly.io instance, keeping both within the free tier.

Server changes:
- Refactor signaling server to HTTP+WS on same port
- Add POST /api/suggest endpoint for metadata extraction
- Add GET /api/health endpoint
- Add metadata.js module (HTML parsing, OG tags, keyword extraction)
- Update Dockerfile to include new module

Client changes:
- Add content-suggestion service with localStorage settings
- Add useContentSuggestion React hook
- Integrate "Suggest" button into BookmarkForm and InboxItem
- Add Services settings page with toggle, privacy disclosure,
  and custom URLs for both suggestion and signaling servers
- Suggestions are opt-in with explicit privacy notice on first enable

https://claude.ai/code/session_01EFjPe8FV9ChZvAsgUrZ8rY
… and tests

Rename:
- signaling-server/ -> services/
- Package name: hypermark-signaling -> hypermark-services
- Fly app: hypermark-signaling -> hypermark-services
- Update vitest coverage exclude path

Security hardening:
- Add SSRF protection: block private/reserved IPs (RFC 1918, link-local,
  loopback, shared address space, IPv6) via DNS pre-resolution
- Add per-IP rate limiting (default 30 req/min) with X-RateLimit headers
- Add DISABLE_SUGGESTIONS=true env var for signaling-only mode
- Return 403 for blocked URLs, 429 for rate limit exceeded

Client fixes:
- Wire up custom signaling URL from localStorage into SignalingClient
  (previously the Settings UI saved it but SignalingClient never read it)

Tests (80 new):
- services/tests/metadata.test.js: 62 tests covering title/description/tag
  extraction, favicon parsing, meta content helpers, SSRF IP blocking,
  hostname validation, entity decoding, truncation
- src/services/content-suggestion.test.js: 18 tests covering toggle,
  URL configuration, fetch calls, error handling, health checks

Documentation:
- services/README.md: deployment, configuration, API reference,
  security details, signaling-only mode instructions

https://claude.ai/code/session_01EFjPe8FV9ChZvAsgUrZ8rY
@netlify
Copy link
Copy Markdown

netlify Bot commented Feb 5, 2026

Deploy Preview for hypermarkk ready!

Name Link
🔨 Latest commit bc3392a
🔍 Latest deploy log https://app.netlify.com/projects/hypermarkk/deploys/698503380fb5df00087bfa64
😎 Deploy Preview https://deploy-preview-30--hypermarkk.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@pheuberger pheuberger merged commit cdd809c into main Feb 5, 2026
7 checks passed
@pheuberger pheuberger deleted the claude/content-suggestion-service-u2uNI branch February 5, 2026 21:01
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