opsinjs

Offline docs bundle

The whole documentation corpus as one versioned JSON file, for agents and tools that run without network access.

Overview

Not every assistant has a network. Some run inside a locked-down build, some inside a hospital network that does not resolve arbitrary hostnames, and some inside a container with egress disabled on purpose. Those are the environments where a health design system is most likely to be used and least likely to be reachable.

/r/docs.json is one request that returns as much of the corpus as fits inside a size budget: the processed markdown of each page it carries, that page's frontmatter, and the docs version identifying the build it came from. It is capped, and today it is truncated. The response says so about itself. total, included, omitted, truncated and truncationNote are top-level fields, and reading them is the difference between vendoring a corpus and vendoring a portion of one. Fetch the shards for the sections it dropped.

curl -s https://opsinjs.pensievelabs.org/r/docs.json -o opsinjs-docs.json

What is in it

A wrapper of build and budget metadata, then one entry per page carrying the fields a machine needs in order to decide whether that page answers the question in front of it.

{
  name: string
  version: string          // the docs version this bundle was built from
  generator: string
  docsVersion: string      // the same version, under the name the header uses
  generatedAt: string      // ISO timestamp of the build
  site: string
  implemented: boolean     // whether any component has source
  implementedCount: number
  notice: string           // the one paragraph a machine must not skip
  homepage: string
  format: "processed-markdown"
  characters: number       // the page text carried, in JS characters. The
                           // budget is applied in that same unit
  total: number            // pages in the corpus
  included: number         // pages in this response
  omitted: number          // pages the budget dropped
  truncated: boolean
  truncationNote?: string  // present only when truncated
  sections: Array<{…}>     // the sixteen sidebar groups
  shards: Array<{…}>       // where to fetch what was dropped
  componentCounts: Record<string, number>        // component pages per phase,
                                   // of the included pages
  corpusComponentCounts: Record<string, number>  // the same, over the whole
                                   // corpus
  conventions: {…}         // the twin, catalog, roster and search URLs
  pages: Array<{
    url: string            // canonical page URL
    markdownUrl: string    // the same page's .md twin
    slug: string[]
    section: string
    title: string
    description: string
    status?: string        // planned | shipped | deprecated. Component pages
                           // only; absent on every other kind
    kind: string           // component | health | foundation | …
    evidence?: string      // health pages declare this
    aliases: string[]
    implements: string[]
    governedBy: string[]
    usedIn: string[]
    reviewed?: string
    markdown: string       // processed markdown; documentation components
                           // arrive as <PascalCase … /> tags
  }>
}

The page body is markdown, not content, and markdown is the same processed text the .md twins serve. "Processed" is narrower than it sounds: remark has run, so imports are gone and tables and code blocks are markdown, but the documentation components are not rendered. <ComponentPreview name="range-bar" /> arrives as that tag and its attributes are the content. See Raw markdown API for what survives and what does not. A page inside the bundle also omits the per-page block quote that a standalone twin carries about those tags, so nothing in a page body explains them; this paragraph is the explanation.

componentCounts tallies the component pages this response carried and corpusComponentCounts tallies every component page in the corpus. Neither sums to included or to total, and that is not an off-by-one: a release phase belongs to a component, so every page that is not a component page declares none and is counted in neither tally. Read corpusComponentCounts for a question about opsinjs and componentCounts for a question about this response.

The bundle carries the documentation, not the code. Component source lives in the registry items. /r/<name>.json is the item for one of the sixty built components, and the catalog is /r/registry.json. Fetch either separately.

Size and freshness

The bundle is most of a corpus and it is not small. Two habits keep it usable:

Index it, do not paste it. It is meant to be searched, chunked or embedded locally. Putting the entire file in a context window spends the window on the retrieval problem you were trying to avoid. If your tool wants a subset, the shards exist for exactly that and are much cheaper.

Pin the version, then check it. The version field is the point of the whole file. A vendored bundle is a snapshot, and a snapshot that nobody ever compares to the live version quietly becomes wrong. A weekly job that fetches the few bytes of the current version string and warns on a mismatch is enough. Compare included and total at the same time: the set of pages a snapshot carries moves as the corpus grows, so two bundles at the same version are the same text and two at different versions may not even be the same pages.

The most important field on a component page in this bundle is status. A built component reads shipped and the source is in its registry item, and today that covers every component. Every opsinjs component has been audited against WCAG 2.2 AA by its own authors (see ADR 0025), which is not an independent accessibility review, and no component has had a clinical review. shipped means the source installs, and it does not mean an independent review or a clinical review has happened. Nothing here is for a production health surface until a clinician signs it. An agent working from a stale bundle a year from now must be able to discover that the world has moved. The docs version is how it finds out, and status is what will have changed.

Using it in an air-gapped build

Vendor it deliberately

Commit it, or store it as a build artefact with a checksum. A file fetched at build time from a network you do not control is not an offline strategy.

Record where it came from

Keep version and generatedAt visible to whatever consumes the bundle, and surface them in any answer the tool produces. "According to opsinjs docs version X" is a checkable claim; "according to opsinjs" is not.

Refresh on a schedule you own

Treat it like a dependency: a deliberate update with a diff, not a silent refresh.

Fail loudly when it is missing

A tool that falls back to the model's general knowledge when the bundle is absent will produce confident, invented answers about a health design system. Prefer an error.

Verify it worked

It parses, and you know what it left out

node -e "const d=require('./opsinjs-docs.json'); console.log(d.version, d.included, 'of', d.total, d.truncated ? '(truncated)' : '')"

included below total is the budget, not a corrupt download. truncationNote says so in a sentence and the shards array is where to fetch the rest.

A known page is present with its status

Look up a component page and confirm it carries a status, which reads shipped for a built component. A component page present with no status is a bug worth reporting, and a page that is not a component carries none by design. A page absent altogether is usually the cap. Check omitted before reporting anything.

The page bodies are under markdown

Read pages[0].markdown. It is markdown, and it will contain <PascalCase … /> tags; that is the format rather than a fault. Reaching for pages[0].content returns undefined, which is the quietest way a vendored reader ends up indexing an empty corpus.

Troubleshooting

The download is truncated. Check truncated and omitted in the payload first: the server caps this response on purpose and reports the cap in truncationNote. A proxy body-size limit is only the likely cause if the JSON will not parse at all. In that case, prefer a client that streams to disk.

The bundle disagrees with the site. Compare version strings. If they match and the content differs, that is a real bug and worth reporting.

I only need the components. Use /llms-components.txt, which is a fraction of the size.

I need this in an air-gapped environment where even the initial fetch is impossible. Build it yourself: clone the repository and run the site's own generate step. The bundle is produced from the same source you would be cloning.

Next

On this page