Degoog — Search engines

Custom search backends (web, images, video).

Where engines live

Custom engines live in data/engines/ (or DEGOOG_ENGINES_DIR). Each file (or folder with an entry file, depending on how the app loads them) exports a SearchEngine object.

SearchEngine contract

Required:

SearchResult shape:

{
  title: string,
  url: string,
  snippet: string,
  source: string,
  thumbnail?: string,
  duration?: string
}

Optional:

SettingField (same as plugins):

{
  key: string,
  label: string,
  type: "text" | "password" | "url" | "toggle" | "textarea",
  required?: boolean,
  placeholder?: string,
  description?: string,
  secret?: boolean
}

Non-web search types

To export an images or video engine, add a named type export:

export const type = "images";  // or "videos" or "web" (default)

Proxies (proxy-friendly engines)

When users enable a proxy for search (Settings → General), all engine requests can go through that proxy. For your engine to be proxy-correct:

  1. Use the injected fetch. Implement executeSearch(query, page?, timeFilter?, context) and use context.fetch for every outbound HTTP request. If you use global fetch, your requests will not use the user’s proxy.
  2. Declare outgoing hostnames. Export a named outgoingHosts array of hostname strings (no protocol or path). Those hostnames are added to the proxy allowlist so your engine works when the proxy is on. Use ["*"] only if your engine fetches from arbitrary user-configured URLs (e.g. RSS feeds).

Example (semantically correct for proxies):

export const outgoingHosts = ["www.example.com", "example.com"];

export default class MyEngine {
  name = "My Search";
  async executeSearch(query, page = 1, _timeFilter, context) {
    const url = `https://www.example.com/search?q=${encodeURIComponent(query)}`;
    const doFetch = context?.fetch ?? fetch;
    const response = await doFetch(url, {
      headers: { "User-Agent": "my-engine/1.0" },
    });
    const html = await response.text();
    // ... parse and return results
  }
}

Always use const doFetch = context?.fetch ?? fetch; then doFetch(url, opts) so that when context is provided (it is when the app runs your engine), requests go through the user’s proxy when enabled.

Setup

Create data/engines/ (or set DEGOOG_ENGINES_DIR). You can add either a single file (e.g. my-engine.js) or a folder with index.js (or .ts, .mjs, .cjs). The engine id is derived from the filename (without extension) or folder name, with an engine- prefix (e.g. my-engine.js or my-engine/index.js → id engine-my-engine).

Distributing via Store

When distributing an engine via a store repository, add a screenshots/ folder inside the engine folder for the Store card thumbnail and lightbox.

How settings work

  1. Declare settingsSchema — a Configure button appears in Settings → Engines.
  2. User saves; values are stored in data/plugin-settings.json.
  3. configure(settings) is called after save and on server restart if settings exist.
  4. Return an empty array from executeSearch when required settings are missing so the engine contributes no results.

News

News search is not an engine type: the News tab uses RSS feeds configured in Settings → Engines → News. Feed URLs are stored in data/plugin-settings.json.