Pinax

Configuration

Where Pinax stores manifests, caches, logs, and how to override defaults via environment variables.

Pinax keeps everything under a single directory in $HOME and leans on environment variables for the few things that need to vary per machine.

~/.pinax/ layout

~/.pinax/
├── catalog.json          # cached catalog (after `pinax catalog refresh`)
├── cache.db              # SQLite page cache (WAL mode)
├── logs.db               # SQLite tool-call log used by the HTTP log UI
└── servers/
    └── <name>/
        ├── manifest.json # atomic write: sections, page list, tags
        └── index.bin     # BM25 index for search_pages
  • Manifests are written atomically - a half-completed pinax add never corrupts the on-disk state.
  • The page cache is read with TTLs applied at read time, so you can hand- edit cache.db if you want to invalidate everything (pinax cache clear is the supported route).

Environment variables

VariableDefaultWhat it does
PINAX_HOME$HOME/.pinaxroot directory for everything Pinax stores
PINAX_CATALOG_URLGitHub main’s catalog.jsonwhere pinax catalog refresh fetches from
PINAX_USER_AGENTpinax/<version>overrides the User-Agent on outbound HTTP requests
PINAX_CACHE_TTL24hTTL applied to entries in the page cache
PINAX_LOG_LEVELinfoone of debug, info, warn, error
JINA_API_KEYunsetrequired for JS-rendered SPA sites (v0.5+); see JS renderer below

All values can also be set per-invocation with the usual shell prefix:

PINAX_LOG_LEVEL=debug pinax serve --http

JS renderer (v0.5+)

Some docs sites (docs.mono.co, most ReadMe.io, some Mintlify) only paint their content after JavaScript runs. Pinax detects this at the content-density preflight and can route those pages through a JS renderer instead of refusing.

The built-in renderer uses Jina Reader. It’s free for personal use (500 RPM per account, no credit card), but you bring your own key:

# One-time setup - persist in ~/.zshrc or ~/.bashrc
export JINA_API_KEY=jina_...     # https://jina.ai/reader

pinax add https://docs.mono.co/docs

Why BYO instead of a bundled key

Every serious CLI that touches a paid API does BYO (gh auth login, OPENAI_API_KEY, AWS_ACCESS_KEY_ID, docker login). Shipping a bundled key would be:

  1. Extractable from every binary (strings pinax | grep jina_).
  2. Shared - Jina’s 500 RPM quota is account-wide, so every user would collide with every other user.
  3. Against Jina’s ToS, which forbids redistributing API keys.
  4. Impossible to rotate without breaking every existing install.

One env var, once, is a fair trade.

At runtime

The chosen renderer is written into the manifest, so pinax serve and the get_page MCP tool route through it too - but the server also needs JINA_API_KEY in its environment. For Claude Desktop that means adding an env block to claude_desktop_config.json; every client page (Claude Desktop, Cursor, etc.) shows the exact shape.

When the key is missing at runtime, get_page returns a structured RENDERER_UNAVAILABLE error with a suggestion the agent can act on.

Opting out

Don’t want to send URLs to a third-party service? Pass --renderer=off to pinax add. SPA sites will refuse instead of escalating.

Cache control

pinax cache clear                  # wipes the page cache
pinax cache clear --older-than 7d  # only entries last fetched > 7d ago

The cache is safe to delete by hand; Pinax will rebuild it on the next get_page call.

Tuning a crawl

When pinax add <url> is too aggressive or too thin for your docs site:

pinax add <url> --max-pages 800            # raise the crawl ceiling
pinax add <url> --exclude '/blog/' --exclude '/legal/'   # skip prefixes
pinax add <url> --no-preflight             # bypass the density gate

--no-preflight is the escape hatch for sites that fail the content density check but you want indexed anyway. The check is documented in Troubleshooting.

For JS-rendered SPAs (Mintlify, ReadMe.io, custom React docs) don’t use --no-preflight - use the renderer instead. See JS renderer above.

Pinax