# Writing Custom Ingestion Binaries for Kitpicks

This guide explains how to author an ingestion binary that Kitpicks can use as a
custom source. A custom source is an executable that generates Markdown; the
reservoir runs it on its fetch schedule and ingests the Markdown files it
produces.

> **Security**: Your binary runs periodically and autonomously with access to
> the machine. Only install trusted scripts.

## The contract

- **Executable bit**: the binary must be executable.
- **Working directory**: the binary is launched from a fresh temporary
  directory containing an `outs/` folder.
- **Environment**: `RES_CHANNEL_ID` is set to the channel id.
- **Arguments**: extra fetch parameters are passed as `key=value` CLI
  arguments.
- **Output**: write one `.md` file per item into `outs/`. Optional
  supplementary files go in a same-named subdirectory.

## Frontmatter requirements

Each Markdown file's frontmatter should include:

- `pubDate` — the item's publication date (required for entry creation).
- `url` — the item's canonical URL (optional). When omitted, the entry links
  to the item's own Markdown file (as a `file://` link) in the Kitpicks UI.
- `title` — the entry title (optional; falls back to the first `# Heading` in
  the body).

## Minimal example

A Node script that writes one Markdown file to `outs/`:

```js
#!/usr/bin/env node
import { writeFileSync, mkdirSync } from "node:fs";
import { join } from "node:path";

mkdirSync("outs", { recursive: true });

const item = `---
url: https://example.com/weather/2026-09-02
pubDate: 2026-09-02T12:00:00Z
title: Today's Weather
---

Sunny with a high of 72°F.
`;

writeFileSync(join("outs", "weather.md"), item);
```

## References

- Working example: [res/examples/weather-fetcher.mjs](https://github.com/guthriec/res/blob/main/examples/weather-fetcher.mjs)
- Fetcher implementation: [res/src/fetchers/custom.ts](https://github.com/guthriec/res/blob/main/src/fetchers/custom.ts)
- User-facing documentation: <https://kitpicks.com/docs/custom-scripts>

## Updating a script

To update an existing custom source's binary, open its **Edit Source** screen
and re-drop the new executable. The updated binary replaces the script in
place — the source keeps its name and fetch schedule. When adding a new source,
if the script's name collides with an existing custom source, the Add Source
screen flags the conflict and links to edit the existing source.