---
title: Publish a registry
description: Build and host versioned chkit templates containing editable schemas and ingestion source.
---

A registry distributes source files, package requirements, and provider metadata as static JSON artifacts.

## File format

chkit uses the [shadcn registry envelope](https://ui.shadcn.com/docs/registry/registry-json) with `registry:item` items and `registry:file` files. chkit-specific metadata lives under `meta.chkit`.

Format version 1 supports self-contained TypeScript templates. It does not resolve `registryDependencies`, transform UI imports, or run template installation hooks. Unknown fields and unsupported item types fail validation. A general shadcn UI registry is not a compatible chkit registry.

## Create a source template

For a minimal example, save this as `registry/demo/index.ts`:

```ts
import { definePipeline, defineStream, rawRows, rawTable } from '@chkit/plugin-ingest'

export const demoEventsRaw = rawTable({ database: 'default', name: 'demo_events_raw' })

const events = defineStream({
  id: 'demo.events',
  destination: demoEventsRaw,
  async *read() {
    yield { rows: rawRows([{ id: 'example', message: 'Registry installed' }], (event) => event.id) }
  },
})

export const demo = definePipeline({ id: 'demo', streams: [events] })
```

Use relative imports inside a larger provider directory so it remains movable with `chkit add --path`. The entry must explicitly export every schema object and active pipeline listed in the manifest; wildcard exports do not satisfy the builder's export check.

## Declare the catalog

Save this as `registry/registry.json`:

```json
{
  "$schema": "https://ui.shadcn.com/schema/registry.json",
  "name": "example-providers",
  "homepage": "https://example.com",
  "items": [
    {
      "name": "demo",
      "type": "registry:item",
      "title": "Demo",
      "description": "A local fixture source for testing registry installation.",
      "dependencies": [
        "@chkit/core@^0.2.0-beta.8",
        "@chkit/plugin-ingest@^0.2.0-beta.8"
      ],
      "files": [
        {
          "path": "demo/index.ts",
          "type": "registry:file",
          "target": "src/integrations/demo/index.ts"
        }
      ],
      "meta": {
        "chkit": {
          "formatVersion": 1,
          "version": "0.1.0",
          "language": "typescript",
          "license": "MIT",
          "chkit": "^0.2.0-beta.8",
          "ingest": "^0.2.0-beta.8",
          "clickhouse": ">=25.3.0",
          "root": "src/integrations/demo",
          "entry": "index.ts",
          "exports": ["demoEventsRaw", "demo"],
          "resources": [
            {
              "name": "events",
              "description": "One fixture event per full read.",
              "scopes": [],
              "strategy": "full"
            }
          ],
          "env": {}
        }
      }
    }
  ]
}
```

Each source `files[].path` is relative to the manifest's directory. `target` is the default consumer-project path and must sit inside `meta.chkit.root`. `entry` is relative to that root. Paths must be normalized relative paths without `..` segments.

The item name `registry` is reserved because `registry.json` contains the catalog.

## Metadata and dependencies

| Field under `meta.chkit` | Meaning |
|---|---|
| `formatVersion` | Registry metadata format; currently `1` |
| `version` | Semantic version of this template; immutable after publication |
| `changelog` | Newest-first version history with a semantic `version` and nonempty `changes` array per entry; optional for custom and legacy items, required for official current manifests |
| `language` | Currently `typescript` |
| `license` | License for the copied source |
| `documentation` | Optional HTTP(S) URL of the app's integration guide; shown by CLI list and inspect |
| `logo` | Optional HTTP(S) URL of the app's logo; included in discovery metadata |
| `chkit`, `ingest`, `clickhouse` | Supported CLI, ingestion package, and ClickHouse version ranges |
| `root`, `entry`, `exports` | Installation directory, provider entry, and explicit public exports |
| `resources` | Resource names and descriptions, read scopes, sync strategy (`full`, `timestamp`, or `cursor`), optional `title`, default `table`, and `endpoints` with `method`, `path`, and provider `documentation` URL |
| `authentication` | Authentication `method`, required `env` names, ordered `setup` steps, and the provider's credential setup `documentation` URL |
| `views` | Derived views with a `name`, source resource `source`, and `description`; these reuse synced records |
| `sync` | Sync `description`, external `schedule`, and `deletions` behavior |
| `env` | Environment variable names mapped to example values, such as `{"ATTIO_API_TOKEN": ""}` |
| `fileHashes` | SHA-256 values keyed by target path; generated by the builder |

Resource strategies describe selection: `full` performs complete scan cycles, including scans with completion checkpoints; `timestamp` selects time windows; `cursor` follows checkpointed provider state, such as a change token or a resumable snapshot traversal. Describe restart behavior, cursor lifetime, and deletion handling in `sync`. Metadata does not configure the runtime strategy. Older strict registry clients accept only `full`; templates advertising new labels must require a CLI version that accepts them.

Keep a cumulative integration changelog in `meta.chkit.changelog`:

```json
{
  "version": "0.2.0",
  "changelog": [
    {
      "version": "0.2.0",
      "changes": ["Give People and Companies independent ingestion streams."]
    },
    {
      "version": "0.1.2",
      "changes": ["Include fixture tests and expanded authentication and resource metadata."]
    }
  ]
}
```

The first entry matches the template version, and subsequent versions descend without duplicates. Retain notes for earlier releases, including migration details users need when upgrading. CLI inspect, integration guides, and agent-readable Markdown display this same history. Historical artifacts without the optional field remain valid. Older strict clients reject unknown metadata fields; current templates using `changelog` require a supporting CLI version.

Declare npm dependencies as `package@semver-range`. Every item must include `@chkit/core` and `@chkit/plugin-ingest`. Git URLs, local dependencies, and package-manager aliases are outside this format.

Use blank values for secrets in `env`. Never include credentials in metadata or source artifacts. State resource limitations in the item's description and copied README, including deletion behavior and inaccessible API families.

### Package fixture tests

Keep tests under the provider's `tests/` directory and mark their file entries with `role: "test"`:

```json
{
  "path": "demo/tests/demo.test.ts",
  "type": "registry:file",
  "target": "src/integrations/demo/tests/demo.test.ts",
  "role": "test"
}
```

These files receive content hashes like other source files; the installer includes them only with `--with-tests`. Optional item-level `devDependencies` lists test tooling, such as `@types/bun@^1.2.0`, using the same `package@semver-range` format. Those dependencies are added to the consumer's development dependencies only when tests are selected.

Tests should use fixture payloads and mocked service clients so consumers can run them without provider credentials. Avoid repository-relative imports, unpublished workspace tooling, and live-database prerequisites in the distributed test set. Repository-only packaging or database tests can live in the same source folder, excluded from the manifest's files.

## Build and test locally

```sh
chkit registry build registry/registry.json --output ./registry-output
chkit registry list --registry ./registry-output
chkit registry inspect demo --registry ./registry-output
```

The build emits:

```text
registry-output/
  registry.json
  demo.json
  demo/
    0.1.0.json
```

`registry.json` is the discoverable catalog. `demo.json` points consumers to the current item contents. `demo/0.1.0.json` contains that specific version. Built items include source content and its hashes, so installation does not need access to the source repository.

From a separate test project with `package.json`, preview installation and then inspect the copied code:

```sh
chkit add /absolute/path/to/registry-output/demo/0.1.0.json --dry-run
chkit add /absolute/path/to/registry-output/demo/0.1.0.json --yes
chkit ingest list
chkit generate --name add-demo
```

Registry validation checks packaging and declared entry exports. Test each provider's pagination, errors, identities, and replay behavior with fixtures; validate the generated schema and queries against a development database before publishing.

## Publish immutable versions

Host the output directory on an HTTP(S) static host. Consumers can use its catalog URL with `--registry` or install a built item URL directly.

Keep every published `<name>/<version>.json` available. The builder rejects an attempt to write different bytes to an existing version file. Increment `meta.chkit.version` for any released template change, including source, dependencies, or metadata. Preserve older version files when building a deployment from a clean checkout; an empty output directory alone cannot establish what was previously published.

### Provider layout in the chkit repository

Each official provider has one self-contained directory:

```text
registry/
  attio/
    manifest.json
    README.md
    index.ts
    client.ts
    config.ts
    pipeline.ts
    sources/
      objects.ts
      object-attributes.ts
      records.ts
      lists.ts
      list-attributes.ts
      entries.ts
      notes.ts
      tasks.ts
      members.ts
    tests/
      attio.test.ts
      fixtures.ts
      install.e2e.test.ts
    releases/
      0.1.0.json
      0.1.1.json
      0.1.2.json
```

Each `sources/` module keeps a resource's reader and schema together. Shared request behavior stays in `client.ts`; selection and destination settings stay in `config.ts`.

Recommend normalized raw sync by default: sync provider resources as directly as possible into separate raw collections, preserve their fields and structure, and store source and parent keys for relationships. Readers retrieve and checkpoint data with minimal identity/envelope mapping. Perform field transformations, business mappings, enrichment, joins, and denormalization afterward in ClickHouse SQL views, materialized views, or derived tables. Use a shaped ingestion schema or projected/embedded document model when explicitly requested; retain inseparable bounded provider objects as returned.

Use one pipeline per installation or account, with independent streams per resource type or configured collection. Prefer a raw destination per resource type; share one when resource types have the same provider representation and retain an explicit resource discriminator. Attio People and Companies are separate streams sharing a raw records table; individual people remain records. A child reader may enumerate parents to reach a parent-scoped endpoint, but it owns that discovery and its checkpoints; parent publication does not wait for it. GitHub issues, PRs, comments, reviews, and commits follow this model. Pipeline order cannot become a discovery dependency.

Prefer `paginate()` and the bundled incremental strategies. An ordinary full read uses `fullSync()` and the executor's journaled completion, including empty reads; add provider state only for a justified contract such as sync-token promotion, delayed children, or expensive acknowledged parent traversal. Pipeline factories bind supplied configuration to both readers and strategies. Raw table exports remain setup-time schema definitions. Verify installed modular templates at relocated paths so missing transitive files cannot pass source-only tests.

`paginate()` yields full `{ items, next, metadata? }` pages, including empty terminal pages. Readers use `page.items` for raw rows and explicitly map safe checkpoint metadata to chunk `state` with `cursorState`. A sync-token adapter retains the input token during pagination and promotes its terminal replacement only with acknowledged rows. Metadata needs no custom pagination wrapper; `paginate` owns each request attempt and validates continuation cycles before exposing a page. Provider-specific token recovery and parent discovery remain reader responsibilities.

`manifest.json` contains one registry item. Its source paths are relative to the provider directory (for example, `sources/notes.ts`); target paths still use the full consumer path such as `src/integrations/attio/sources/notes.ts`. The repository's catalog loader discovers these provider-local manifests and aggregates them for the build, CLI artifacts, and documentation. `bun scripts/build-registry.ts` builds the official catalog; it does not require a handwritten catalog at the registry root.

Released artifacts are committed under `registry/<name>/releases/<version>.json`. The official CLI reads provider directories and manifests from GitHub's `main` branch and installs those release artifacts directly. The documentation build also copies that history into `apps/docs/public/r/<name>/` before building the current catalog and latest aliases. History and the source are colocated without installing release files into consumer projects.

An artifact already present in the PR's merged base is immutable: preserve its exact bytes and path. Each integration contributes at most one new version artifact per PR. Choose its version above the latest merged release when first changing distributed source or metadata; subsequent edits in the same unmerged PR update that draft's source and current changelog entry without another version bump. Intermediate PR revisions do not become separate release artifacts or changelog entries.

Regenerate the draft at the same version with the repository's release command:

```sh
bun run registry:release -- --base origin/main attio
bun run check:registry-releases -- --base origin/main
```

The base defaults to `origin/main`; use the actual PR base when it differs. Pass provider names to refresh selected integrations, or omit names to refresh all current drafts. The release command regenerates artifacts from source and refuses to replace versions already in the base. The check rejects changed or deleted published artifacts, multiple new versions for one integration, and a current artifact that does not match its source and manifest. The same draft can be regenerated after every edit, including after it has been committed or pushed to the PR branch. Once merged, further template changes require a new version and changelog entry in another PR.

Commit the generated draft with its source, manifest, and changelog updates. Do not hand-edit artifact content or commit generated latest aliases. Adding a provider or template version does not require an npm package release; changes to CLI behavior do. The docs build also publishes a static catalog at `https://chkit.obsessiondb.com/r/registry.json` for web and custom-registry use.

## Add an app to the official documentation

Each provider declared in `registry/<name>/manifest.json` has an MDX guide at `apps/docs/src/content/docs/integrations/<name>.mdx` so the shared resource and changelog components render. Set its title to `Integrating ClickHouse with <App title>` and write a specific one-sentence description. Give the sidebar a short app label.

Set `meta.chkit.documentation` to `https://chkit.obsessiondb.com/integrations/<name>/`. Every official app requires a provider logo: store the official asset in `apps/docs/public/logos/`, record its source in that directory's `README.md`, and set `meta.chkit.logo` to its full HTTPS URL on the docs site. Preserve the asset's proportions and brand colors.

Every official manifest includes authentication setup steps, resource titles and destination tables, provider endpoint references, derived views, sync/deletion metadata, and an integration changelog. Credential setup must explain where an administrator creates a token in the source system, which permissions it requires, and how the execution environment receives it. Link to the provider's current instructions and verify the UI path before publishing.

Every guide also explains installation, migrations, raw and projected fields, pagination, repeat runs, failure recovery, unsupported data, and scheduling. Verify the claims against the installed readers and schema. A name-swapped introduction alone is not a complete integration guide.

Use `RegistryReference` in MDX to render shared reference sections from the provider manifest:

```mdx
import RegistryReference from '../../../components/RegistryReference.astro';

<RegistryReference name="attio" section="authentication" />
<RegistryReference name="attio" section="scopes" />
<RegistryReference name="attio" section="resources" />
<RegistryReference name="attio" section="changelog" />
```

The other sections are `overview`, `views`, and `sync`. The raw-Markdown build expands the same components for agents. The `resources` section documents every declared resource; handwritten guides must include each exact resource name in backticks. Include `changelog` under a Changelog heading in each official integration guide. Keep the explanation of provider-specific behavior as prose alongside the generated reference tables.

The [integration list](/integrations/), its agent-readable Markdown, CLI discovery, and search structured data read the same manifest. The docs build checks that every official item has its guide, description, resource coverage, and required local logo asset. New guides also enter site search, the sitemap, and `llms.txt` automatically.

```sh
bun run scripts/check-registry-docs.ts
bun run --cwd apps/docs build
```

Preview the app listing and guide in both themes, check the rendered resource tables and changelog, and verify the guide appears in `apps/docs/dist/_raw/index.md` and `apps/docs/dist/llms.txt`. Changes to released template metadata require a new version just like source changes; edits to an unmerged draft update its one version artifact.

## Related pages

- [`chkit registry`](/cli/registry/): command reference and build flags.
- [`chkit add`](/cli/add/): consumer installation behavior.
- [Test a source](/api-sync/testing/): ingestion correctness beyond packaging.
- [Provider templates](/api-sync/templates/): template ownership and customization.
