# opencatalog.sh — Agent Guide

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

opencatalog.sh maps paid software to serious FOSS alternatives. Every claim
is grounded with a citeable source, every install path is verified, every gap
is labeled. The HTML site and the JSON API serve the same content — there is
no separate API model.

This file is the single entry point for AI agents and LLMs. It lists every
endpoint, every available record slug, the schema, the trust model, and the
submission flow. Fetch this file, then fetch `/api.json` for the data.

## Endpoints

| Method | Path | Returns | Notes |
|--------|------|---------|-------|
| GET | `/api.json` | Full envelope | All 4 paid products, 6 alternatives, 3 categories, 4 licenses in one response |
| GET | `/api.schema.json` | JSON Schema (draft 2020-12) | Validate envelopes client-side |
| GET | `/api/paid/{slug}` | Single `PaidProduct` | Available slugs: `adobe-photoshop`, `figma`, `notion`, `obsidian` |
| GET | `/api/alt/{slug}` | Single `Alternative` | Available slugs: `appflowy`, `gimp`, `joplin`, `krita`, `logseq`, `penpot` |
| GET | `/api/category/{slug}` | Single `Category` | Available slugs: `image-editing`, `note-taking`, `ui-design` |
| GET | `/api/license/{slug}` | Single `LicensePrimitive` | Available slugs: `agpl-3.0`, `gpl-3.0`, `mit`, `mpl-2.0` |
| POST | `/api/submit` | Submission receipt | Submit a candidate entry (see below) |

All GET endpoints are static (CDN-served) and return `Cache-Control: public, max-age=3600, s-maxage=86400`.

## 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).

## Trust model

### 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

## Submitting new entries

### 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.

## HTML page map

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 |

## How to use this site as an agent

1. **Discover what exists**: fetch `/api.json` for the full envelope, or read the slug lists in the endpoint table above.
2. **Get a single record**: fetch `/api/paid/{slug}`, `/api/alt/{slug}`, `/api/category/{slug}`, or `/api/license/{slug}`.
3. **Validate**: fetch `/api.schema.json` and validate any response against it.
4. **Submit a new entry**: `POST /api/submit` with a candidate body (see submission section above).
5. **Link a human to results**: use the HTML page map above — the HTML and JSON are the same content.

## 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/skills.md
