manifest.json Reference
Every source adapter includes a manifest.json at the root of its folder. The manifest declares what the source is, how often it runs, and which preference fields the user fills in during setup. The Mac app reads the manifest to list the source in the directory, render its setup wizard, and schedule its syncs.
Full example
Section titled “Full example”{ "id": "hacker-news", "name": "Hacker News Upvotes", "description": "Index stories you've upvoted on Hacker News.", "version": "1.0.0", "author": "matt", "category": "reading", "schedule": "every 6 hours", "config": { "username": { "label": "HN Username", "type": "text", "placeholder": "pg" } }, "needs_browser": false, "kind": "feed", "transport": "http", "document_semantics": "bookmark"}Fields
Section titled “Fields”| Type | string |
| Required | Yes |
| Pattern | ^[a-z0-9-]+$ (lowercase, digits, hyphens) |
A stable identifier for the source type, unique within a catalog. Used as the source’s namespace in storage and as the folder name in the catalog repo.
| Type | string |
| Required | Yes |
Human-readable display name, shown in the Mac app and the source directory. Keep it short and descriptive — "Hacker News Upvotes", not "HN Upvoted Stories Indexer v2".
description
Section titled “description”| Type | string |
| Required | No |
A one-line description shown in the directory listing. Describe what gets indexed, not how.
| Type | string — emoji or URL |
| Required | No |
A single emoji or an HTTPS URL to a square icon (PNG/SVG). Shown alongside the source name.
version
Section titled “version”| Type | string — semver |
| Required | Yes |
The source-adapter version, following semver. The Mac app uses it for update detection — bump it on each meaningful change so added instances can be offered the update.
author
Section titled “author”| Type | string |
| Required | No |
Who wrote the source adapter. Shown in the directory; useful for attribution in community catalogs.
category
Section titled “category”| Type | string |
| Required | No |
A grouping for the directory. Conventional values: reading, social, finance, dev, media, local, research, utility.
schedule
Section titled “schedule”| Type | string |
| Required | No (default: on demand) |
Human-readable sync cadence — "every 6 hours", "daily", "hourly", "on demand". The Mac app parses it into a timer. Scheduling, retry, and sleep/wake handling all live in the Mac app; the cloud does not schedule syncs.
"schedule": "daily"A live-only source that exposes only query() (no batch sync) can set "schedule": null.
config
Section titled “config”| Type | object |
| Required | No (default: {}) |
The preference fields shown in the source’s setup wizard. Each key becomes a property on ctx.config; each value describes the input.
"config": { "username": { "label": "HN Username", "type": "text", "placeholder": "pg" }, "feeds": { "label": "Feed URLs", "type": "text[]" }}Each field descriptor accepts:
| Key | Type | Description |
|---|---|---|
label | string | Display label in the setup UI. |
type | string | Input type — text, text[], url, url[], path, number, boolean. |
placeholder | string | Optional example text. |
auth (proposed)
Section titled “auth (proposed)”| Type | object |
| Required | No |
Auth fields the source needs (for upstreams that require login). Like config, the keys name the fields — but the values are stored in the macOS Keychain, not in the cloud, and surfaced to the source adapter at sync time via ctx.credentials.
"auth": { "api_key": { "label": "API Key", "type": "password" }, "cookie": { "label": "x.com session", "type": "browser_cookie:x.com" }}Auth field types: password, oauth:<provider> (e.g. oauth:google), browser_cookie:<domain>. The setup wizard renders the appropriate native prompt. Nothing here is sent to or stored by the cloud.
needs_browser
Section titled “needs_browser”| Type | boolean |
| Required | No (default: false) |
Whether the source adapter requires a Playwright browser (via ctx.browser). true for upstreams with no API that need a logged-in session (social bookmarks); false for plain HTTP/feed sources.
needs_browser also decides where the source runs. A needs_browser: true source runs on the Trove Mac app (badged “Runs on your Mac”), which launches the headless browser session — a real browser is one of the things Trove’s cloud can’t provide. Plain HTTP/feed sources (needs_browser: false) are eligible to run in Trove’s cloud by default. See Where sync runs.
Denormalized classification fields
Section titled “Denormalized classification fields”These optional fields classify the source for the directory and for how Trove treats its documents. They are denormalized hints — Trove can usually infer them, but declaring them keeps listings accurate and document handling explicit.
| Type | string |
| Required | No |
What kind of upstream this is. Conventional values: feed (RSS/Atom/JSON feed), account (a logged-in account’s content), files (local filesystem), api (a structured API). Drives directory grouping and setup hints.
"kind": "feed"transport
Section titled “transport”| Type | string |
| Required | No |
How the source adapter reaches the upstream: http (plain fetch), browser (Playwright session), or fs (local filesystem). Should agree with needs_browser — transport: "browser" implies needs_browser: true.
"transport": "http"document_semantics
Section titled “document_semantics”| Type | string |
| Required | No |
The default content type for documents this source produces, when not set per-document. Conventional values: text, transcript, highlight, bookmark. A document’s own contentType always wins; this is the fallback applied to documents that omit it.
"document_semantics": "bookmark"live (proposed)
Section titled “live (proposed)”| Type | boolean |
| Required | No (default: false) |
If true, the source adapter also exports a query(ctx) function for real-time requests answered on demand (live search, stock quotes) in addition to — or instead of — scheduled sync.
Minimal manifest
Section titled “Minimal manifest”A no-auth feed source needs only a handful of fields:
{ "id": "my-blog", "name": "My Blog", "version": "1.0.0", "schedule": "daily", "config": { "feedUrl": { "label": "Feed URL", "type": "url" } }, "needs_browser": false}See also
Section titled “See also”- SDK Reference — how
config,schedule, andneeds_browsersurface insync(ctx) - Cursors and Feeds — the feeds model and per-feed cursors
- Sources concept — source lifecycle and the no-credentials policy