# Riposte Marketplace Official catalog of **integrations** for the [Riposte](https://gitea.riposte-labs.com/f3nris/riposte) SOAR platform. Riposte syncs this repository (a "git source") and lists every integration it finds so operators can install them in one click. Nothing here is executed at sync time — Riposte only reads manifests and scripts. --- ## How discovery works When Riposte syncs a source it does a shallow `git clone` of the selected branch and walks the whole tree looking for files named exactly **`manifest.yaml`**. - **Each `manifest.yaml` is one integration.** Its containing directory is the package root. Put one integration per directory. - **Scripts** are collected from the package directory **and** its `scripts/` subdirectory: every `*.py` file becomes a command implementation, keyed by filename without the extension. So a command with `id: get_ip_report` is backed by `get_ip_report.py`. - Files **not** named `manifest.yaml` are ignored as integration roots, so templates like `manifest.example.yaml` are never ingested. ``` integrations/ └── / ├── manifest.yaml # required — the integration definition └── scripts/ # optional — only for script-based commands └── .py ``` --- ## Manifest schema (`manifest.yaml`) ```yaml id: my_integration # required, unique, slug name: My Integration # required, human name version: 1.0.0 # semver description: What it does. changelog: "1.1.0 — notes" # optional: shown on update so operators can assess risk category: enrichment # free text (enrichment, containment, ticketing…) # Per-instance configuration the operator fills when creating an instance. # JSON-Schema shape: { properties: {...}, required: [...] }. config_schema: properties: base_url: type: string # string | number | boolean description: API base URL default: https://api.example.com/v1 api_key: type: string description: API key x-soar-sensitive: true # stored encrypted in the vault, never returned required: - api_key # Authentication methods, referenced by commands via `auth_ref`. auth: - id: apikey type: api_key # api_key | bearer | basic | oauth2_client_credentials in: header # header | query name: x-apikey # header/query parameter name value_template: "{{secret}}" # {{secret}} is replaced by the secret_field value secret_field: api_key # which config_schema field holds the secret commands: # --- Request-based command (recommended, no code) ----------------------- - id: get_ip_report name: Get IP report description: Reputation for an IP address. inputs_schema: properties: ip: type: string description: IP address to look up required: - ip outputs_schema: properties: {} request: method: GET # GET | POST | PUT | PATCH | DELETE path: /ip/{ip} # {ip} is filled from inputs query: [] # input names sent as query params body: [] # input names sent as JSON body fields auth_ref: apikey # --- Script-based command ----------------------------------------------- # Omit `request` and provide scripts/.py instead. The script receives the # resolved inputs + instance config and MUST print one JSON object to stdout. - id: enrich_custom name: Custom enrichment description: Runs scripts/enrich_custom.py in a sandbox. inputs_schema: properties: indicator: type: string required: - indicator outputs_schema: properties: {} ``` A command is **request-based** when it has a `request:` block, or **script-based** when a matching `scripts/.py` exists. Prefer request-based commands: they need no sandbox and are easier to audit. --- ## Adding this catalog to Riposte In Riposte → **Integrations → Marketplace → Add source**: | Field | Value | |-------|-------| | Name | Official marketplace | | Git URL | `https://gitea.riposte-labs.com/f3nris/riposte-marketplace.git` | | Branch | `main` | | Provider | **Gitea** (sets the right auth scheme for private repos) | | Token | a read-only token if the repo is private; leave empty if public | Then **Sync**. Discovered integrations appear in the marketplace, ready to install. --- ## Contributing an integration 1. Create `integrations//manifest.yaml` (one directory per integration). 2. Add `scripts/.py` only for script-based commands. 3. Bump `version` (semver) on every change — Riposte tracks versions per source. 4. Validate the YAML parses and `id`/`name` are set. 5. Open a merge request. See [`templates/manifest.example.yaml`](templates/manifest.example.yaml) for a fully-commented starting point (that file is intentionally **not** ingested).