Knowledge base
Collections from outside sources, imports and connectors, runbooks with stable step ids, the public site, grants, and the /kb API.
A knowledge base article is never a document and never belongs to a company. Collections hold reference articles from outside sources: a vendor’s help center read by its own structure, a website crawled by sitemap or from a starting page, a wiki or another system’s export brought in as a zip or a folder, with their pictures and the PDFs and Word documents they link.
Bringing documentation in
Imports upsert on (collection, source key), so a newer export updates what changed and nothing else. Every converter and every stored body is held to one standard: an imported article must read as its source did.
- Numbered steps keep their numbers and keep counting. A picture, a note, or a caption between two steps belongs to the step above it. A list restarts only where the source restarts.
- A source site’s own navigation is never kept: breadcrumbs, “Contents” lists of anchor links, print and share buttons. Trove KB builds its own outline from the headings.
- A section title the source numbers is a heading, not an empty list item.
images/andfiles/folders in an archive are attachments; a crawled page’s pictures are kept by their address; the original document is kept and offered.- Categories come from the source’s structure, and a folder named on import overrides them.
Check a new source by importing it and reading three articles with steps against their originals before importing the rest.
Admin → Knowledge base drives imports from the browser in pieces, resumable. An archive already on the server can be imported without the browser:
npx tsx --conditions react-server scripts/kb-import.ts "<collection>" <file.zip>
Run it where STORAGE_PATH is the deployment’s own uploads volume: the pictures in the archive are written there.
Runbooks
A runbook is an article whose lists are a procedure. Its steps are read from the body on every save: the items of every top-level list that is a procedure, in order across headings, each as { id, text, note?, canned? }.
The id is stable: write it yourself at the end of the item as {#my-id} ([a-z0-9-]{1,40}), or let Trove KB mint one on first save, after which it is in the stored body and kept across edits. A system that tracks progress through a runbook keys its state by id and keeps that state on its own side; Trove KB stores none. note is whatever was nested under the item; canned is the name in the first @canned:[Name] token of the step. Two steps with one id are refused.
The public site
/pub/kb is for readers who have not signed in. It belongs on a hostname of its own, such as kb.yourdomain.com, behind a proxy that passes /pub/kb, /_next/static/, the logo, and the favicon, and refuses everything else. See Cloudflare for the nginx block.
- Each collection has Show on the public site, off by default. An article can be held back from its own page.
- Keyword rules (phrases or regular expressions, by title, category, or file type) and per-category switches keep the rest off, and apply to what arrives later.
- Under Admin → Settings → Public knowledge base, choose who is admitted: anyone who can reach it, or only visitors from listed addresses. Pair it with a Cloudflare Access Bypass policy for the same ranges.
- Behind Access, name the team and the application’s audience tag and readers keep favorites and votes: Trove KB checks the token Access adds to each request against the team’s published keys and knows the reader by a hash, with no account of its own.
- Customers. Give a company its sign-in email domains on its edit page. A visitor Access names from one of them is placed with that company and reads as its people would: the collections for every company, plus those kept to theirs, with the company’s logo in the corner. A visitor nobody named sees what is for everyone alone. Nothing of the visitor is stored; the address is read, matched, and dropped.
- Your name on it. Under Admin → Branding the public site’s credit can carry your own name: “Powered by Trove KB | Your MSP”. The product stays named and the license stays put.
- Admin → Portal is the setup page: what is set, whether the published address answers, whether an Access token has verified since the process started, which collections are on the site and to whom they are kept, which companies place their people, and the branding it carries, with the Cloudflare steps inline.
Grants
A person granted a collection reads it, and with can_write writes to it in the app, whatever companies it is kept to; administrators need no grant. An account can be made ahead of a person’s first sign-in so the grant is waiting for them. Under Admin → Knowledge base → API access, an administrator sets one key’s access to every collection at once: D (only what its companies allow), R, RW, and whether the key may keep reactions there.
API
GET /kb/collections?writable=true
GET /kb/collections/:id?kind=
GET /kb/search?q=&collection_id=&category=&kind=&limit=&cursor=
GET /kb/articles?collection_id=&category=&subcategory=&kind=&updated_since=&sort=&dir=&limit=&cursor=
GET /kb/articles/:id full body, kind, steps, external_id, source_url, public_url
PUT /kb/collections/:id/articles/:external_id upsert (write scope + write grant)
DELETE /kb/collections/:id/articles/:external_id archive (write scope + write grant)
PUT /kb/collections/:id/grants/users/:userId { can_write? }
DELETE /kb/collections/:id/grants/users/:userId
PUT /kb/collections/:id/grants/api-keys/:keyId { can_write?, reactions? }
DELETE /kb/collections/:id/grants/api-keys/:keyId
Every article item carries kind, source_type (md, html, pdf, docx, txt), public, favorites, and helpfulness. audience=public narrows a read to what the public site shows. A PUT answers 201 when it created the article and 200 when it replaced it.
Favorites and votes for a named reader
With the reactions scope and the header X-Trove-Reader: <email> (never stored; a key is derived from it, the same one the public site uses behind Access):
GET /kb/articles/:id/reactions -> { favorites, helpful_up, helpful_down, helpfulness, mine }
PUT /kb/articles/:id/favorite -> 204 DELETE -> 204
PUT /kb/articles/:id/vote { helpful: true|false } -> 204 DELETE -> 204
GET /kb/favorites?limit=&cursor=
Webhooks kb.article.upserted and kb.article.archived are sent for writes through the API, MCP, or the in-app editor.