# opencatalog.sh — Agent Skills

> Schema version: `0.1.0` · Generated: 2026-07-05T03:50:36.025Z

opencatalog.sh maps paid software to serious FOSS alternatives. This file
describes the same site as `/agents.md` but organized as capabilities an
agent can invoke. Each skill lists the endpoint, inputs, outputs, and an
example. The underlying data, schema, and trust model are identical.

## Skill: Query the full catalog

**Endpoint**: `GET /api.json`

Returns the complete catalog as a single JSON envelope. Use this when you
need to browse, search, or cross-reference records without multiple
requests.

**Output**: `ApiEnvelope` — `{ schemaVersion, generatedAt, paidProducts[], alternatives[], categories[], licenses[] }`

**Current contents**:
- 4 paid products: `adobe-photoshop`, `figma`, `notion`, `obsidian`
- 6 FOSS alternatives: `appflowy`, `gimp`, `joplin`, `krita`, `logseq`, `penpot`
- 3 categories: `image-editing`, `note-taking`, `ui-design`
- 4 licenses: `agpl-3.0`, `gpl-3.0`, `mit`, `mpl-2.0`

## Skill: Fetch a single record

Fetch one record by type and slug without downloading the full envelope.

| Capability | Endpoint | Output type |
|-----------|----------|-------------|
| Get a paid product | `GET /api/paid/{slug}` | `PaidProduct` |
| Get a FOSS alternative | `GET /api/alt/{slug}` | `Alternative` |
| Get a category | `GET /api/category/{slug}` | `Category` |
| Get a license | `GET /api/license/{slug}` | `LicensePrimitive` |

**Available slugs**:

- Paid products: `adobe-photoshop`, `figma`, `notion`, `obsidian`
- Alternatives: `appflowy`, `gimp`, `joplin`, `krita`, `logseq`, `penpot`
- Categories: `image-editing`, `note-taking`, `ui-design`
- Licenses: `agpl-3.0`, `gpl-3.0`, `mit`, `mpl-2.0`

**On 404**: `{ "error": "not found" }`

## Skill: Validate data against the schema

**Endpoint**: `GET /api.schema.json`

Returns a JSON Schema (draft 2020-12) generated from the Zod source of
truth. Use this to validate envelopes or individual records before acting
on them.

**Output**: `application/schema+json`

## Skill: Submit a new entry

### Endpoint

`POST /api/submit` — public, no auth required. Rate limited to 10 submissions per IP per hour.

### Headers

| Header | Required | Values |
|--------|----------|--------|
| `Content-Type` | yes | `application/json` |
| `X-Submit-Source` | no | `manual \| ai-agent \| scraper \| community-pr` |
| `X-Submit-Identity` | no | Your name or agent identifier |

### Body

The body is a `Candidate` — a discriminated union on `kind`:

- `kind: "paid-product"` — submit a new paid product to map
- `kind: "alternative"` — submit a new FOSS alternative
- `kind: "category"` — submit a new category
- `kind: "license"` — submit a new license entry

The candidate schema is relaxed vs. the curated schema: fields are optional where enrichment fills gaps, `basis` defaults to `claimed`, and `sources` can be empty. A `submitter` block is injected from headers.

### Example: submit a FOSS alternative

```bash
curl -X POST https://www.opencatalog.sh/api/submit \
  -H "Content-Type: application/json" \
  -H "X-Submit-Source: ai-agent" \
  -H "X-Submit-Identity: my-agent" \
  -d '{
    "kind": "alternative",
    "slug": "my-tool",
    "name": "My Tool",
    "description": "A free tool that does X",
    "categories": ["some-category"],
    "replaces": ["some-paid-product"],
    "licenseSlug": "mit",
    "deployment": ["desktop"],
    "installPaths": [],
    "maturity": [],
    "knownGaps": [],
    "sources": []
  }'
```

### Response

| Status | Meaning |
|--------|---------|
| 201 | Submission queued — returns `{ ok, submissionId, slug, kind, message, rateLimit }` |
| 400 | Validation error — returns `{ ok: false, error, errors[] }` |
| 409 | Duplicate slug already pending |
| 429 | Rate limited — returns `Retry-After` header |
| 503 | Redis not configured (server-side issue) |

### Pipeline after submission

Submissions land in a Redis queue. A GitHub Action syncs them every 2 hours:

``
POST /api/submit → Redis queue → GitHub Action writes to staging/ → opens PR → review → enrich → promote → curated/
```

The candidate schema is defined in `src/lib/candidate-schema.ts`. Fetch `/api/submit` (GET) for inline docs.

## Skill: Understand trust and freshness

### Basis field

Every load-bearing fact carries a `basis` value:

| Value | Meaning |
|-------|---------|
| `probed` | Verified by direct probe at the source URL |
| `cited` | Sourced from documentation or official statement |
| `claimed` | Asserted without independent verification |

### Verified records

A record is `verified: true` only if:
- Its license is grounded (probed or cited)
- At least one install path is probed or cited
- Load-bearing workflow facts are not merely claimed

### Freshness

- Every record carries `generatedAt` (ISO 8601 datetime)
- Maturity signals carry `probedAt` and a derived `stale` boolean
- Stale probes are visible — the catalog does not hide decay

## Skill: Link humans to results

The HTML site mirrors the JSON 1:1. Use these URLs when presenting results
to a human:

The HTML site mirrors the JSON 1:1. These URLs are for reference or for presenting results to humans:

### Paid products

| URL | Name |
|-----|------|
| https://www.opencatalog.sh/adobe-photoshop/ | Adobe Photoshop |
| https://www.opencatalog.sh/figma/ | Figma |
| https://www.opencatalog.sh/notion/ | Notion |
| https://www.opencatalog.sh/obsidian/ | Obsidian |

### FOSS alternatives

| URL | Name |
|-----|------|
| https://www.opencatalog.sh/alt/appflowy/ | AppFlowy |
| https://www.opencatalog.sh/alt/gimp/ | GIMP |
| https://www.opencatalog.sh/alt/joplin/ | Joplin |
| https://www.opencatalog.sh/alt/krita/ | Krita |
| https://www.opencatalog.sh/alt/logseq/ | Logseq |
| https://www.opencatalog.sh/alt/penpot/ | Penpot |

### Categories

| URL | Name |
|-----|------|
| https://www.opencatalog.sh/category/image-editing/ | Image editing & raster graphics |
| https://www.opencatalog.sh/category/note-taking/ | Note-taking & knowledge management |
| https://www.opencatalog.sh/category/ui-design/ | UI/UX Design & Prototyping |

### Licenses

| URL | Name |
|-----|------|
| https://www.opencatalog.sh/license/agpl-3.0/ | GNU Affero General Public License v3.0 |
| https://www.opencatalog.sh/license/gpl-3.0/ | GNU General Public License v3.0 |
| https://www.opencatalog.sh/license/mit/ | MIT License |
| https://www.opencatalog.sh/license/mpl-2.0/ | Mozilla Public License v2.0 |

## Schema reference

### Top-level envelope (`ApiEnvelope`)

```
{
  "schemaVersion": "0.1.0",
  "generatedAt": "<ISO datetime>",
  "paidProducts":   PaidProduct[],
  "alternatives":   Alternative[],
  "categories":     Category[],
  "licenses":       LicensePrimitive[]
}
```

### Key types

- **PaidProduct** — a commercial product with `rankedAlternatives[]` linking to FOSS alternatives by slug, `workflows[]`, `exportImport`, `pricingShape`, `sources[]`
- **Alternative** — a FOSS tool with `license` (ref), `deployment[]`, `platforms[]`, `installPaths[]`, `maturity[]` signals, `knownGaps[]`, `replaces[]` (paid product slugs), `categories[]`
- **Category** — groups paid products and alternatives; has `neighborCategories[]`
- **LicensePrimitive** — SPDX-aligned: `spdxId`, `osiApproved`, `permits[]`, `requires[]`, `networkDeploymentNote`
- **Source** — `{ url, label, basis, fetchedAt? }` — every load-bearing fact links to its evidence

### Enums

- `Basis`: `probed | cited | claimed`
- `DeploymentShape`: `desktop | self-hosted | web-deployable | hosted-service`
- `FitTier`: `best-fit | partial-fit | different-philosophy`
- `WorkflowFitStatus`: `supported | partial | missing | unknown`
- `InstallPathKind`: `package-manager | download | container | source | hosted`
- `Platform`: `macos | windows | linux | ios | android | browser | server | docker | kubernetes`
- `PricingShape`: `subscription | one-time | freemium | per-seat | usage-based | bundle-only`

Fetch `/api.schema.json` for the full machine-readable JSON Schema (draft 2020-12).

## Conventions

- All JSON uses `Content-Type: application/json; charset=utf-8`
- Datetimes are ISO 8601 (UTC)
- Slugs are lowercase, hyphen-separated, matching `/^[a-z0-9-]+$/`
- Reserved slugs (cannot be paid product slugs): `alt`, `category`, `license`, `browse`, `about`, `api`, `api.json`, `api.schema.json`

## See also: https://www.opencatalog.sh/agents.md
