Files
riposte-marketplace/templates/manifest.example.yaml
T
f3nris ab63ead00e docs(manifest): document the per-command category
A command can now declare what it is FOR, and Riposte's agent browses the estate
by it: enrich, hunt, inspect, transform, collect, contain, remediate, identity,
notify, ticket, manage.

It is a second axis to the manifest-level `category`, which says what the
product is. All 101 of HarfangLab's commands are "endpoint"; the analyst looking
for a decoder and the one looking for an isolation are not looking in the same
place, and the product axis cannot separate them.

Optional, and the template says why it is worth writing anyway: a command that
declares nothing is classified by Riposte from its id and description, which is
a guess. A value outside the list is ignored and the command classified instead,
so a typo costs the declaration rather than inventing a twelfth category that
nothing else ever lands in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01F8cMrB8KPHVpL47rmKynDQ
2026-09-10 14:26:09 +00:00

127 lines
6.2 KiB
YAML

# =============================================================================
# Integration manifest template — copy to integrations/<id>/manifest.yaml
# This file is named *.example.yaml on purpose so Riposte never ingests it
# (only files named exactly "manifest.yaml" are picked up).
# =============================================================================
id: example_integration # required · unique slug · [a-z0-9_]
name: Example Integration # required · shown in the UI
version: 1.0.0 # semver · bump on every change
description: One-line description of what this integration does.
# changelog: shown to operators when an update is available, so they can assess
# risk before updating. Describe notable/breaking changes for THIS version.
changelog: "1.0.0 — Initial release."
category: enrichment # free text: enrichment | containment | ticketing | ...
# ---------------------------------------------------------------------------
# config_schema — per-instance settings the operator fills in when creating an
# instance. JSON-Schema shape: { properties: {...}, required: [...] }.
# Property fields: type (string|number|boolean), description, default,
# and x-soar-sensitive: true for secrets (encrypted in the vault, never returned).
# ---------------------------------------------------------------------------
config_schema:
properties:
base_url:
type: string
description: API base URL
default: https://api.example.com/v1
api_key:
type: string
description: API key / token
x-soar-sensitive: true
required:
- api_key
# ---------------------------------------------------------------------------
# auth — authentication methods, referenced from commands via auth_ref.
# value_template: {{secret}} is replaced by the value of secret_field.
# api_key in header → name is the header (e.g. x-apikey, Authorization)
# bearer → typically name: Authorization, value_template: "Bearer {{secret}}"
# ---------------------------------------------------------------------------
auth:
- id: apikey
type: api_key # api_key | bearer | basic | oauth2_client_credentials
in: header # header | query
name: Authorization
value_template: "Bearer {{secret}}"
secret_field: api_key
commands:
# Every command declares `risk:` — read | safe_write | destructive. It is what
# the agent approval gate reads: `read` runs unattended, `safe_write` and
# `destructive` sit behind the operator's configured threshold. An omitted
# risk is treated as `destructive`, which is the safe default but makes a
# lookup as noisy as an isolation, so declare it rather than let it default.
# read — only queries; changes nothing outside Riposte
# safe_write — additive or reversible: a comment, a ticket, a tag, a
# sandbox submission, an alert status, a collection job
# destructive — changes the estate or weakens a control: containment and
# its removal, blocklists, exclusions, deletes, identity and
# credential operations, running code on a host
# --- Request-based command (recommended: declarative, no sandbox) ---------
#
# `category` says what the command is FOR. It is what the agent browses the
# estate by: an analyst who wants to decode a rewritten URL and one who wants
# to isolate a host are not looking in the same place, and every command of
# your integration shares the manifest-level `category` above, which describes
# the PRODUCT and cannot separate them.
#
# Optional. A command that declares none is classified from its id and
# description when Riposte projects it into an agent tool — a decent guess,
# and no more than that. Declaring it stops the guessing for that command,
# which is the point of the field: you know, the classifier infers.
#
# enrich reputation or analysis of an indicator by a third party
# hunt search telemetry: events, logs, process trees
# inspect fetch one object in detail — a detection, a host, a record
# transform decode, defang, parse, extract — acting on data, not on an estate
# collect gather artefacts: jobs, forensic packages, dumps
# contain isolate, block, quarantine, suspend
# remediate undo a control or delete: unisolate, unblock, restore, purge
# identity accounts: disable, reset a password, revoke a session
# notify send a message, an e-mail, a page
# ticket create or update a ticket or a case
# manage policies, exclusions, configuration, agent lifecycle
#
# A value outside this list is ignored and the command is classified instead,
# so a typo costs the declaration rather than inventing a category nothing
# else ever lands in.
- id: lookup
name: Lookup indicator
description: Fetches a report for an indicator over HTTP.
risk: read # required in practice · read | safe_write | destructive
category: enrich # optional · what the command is FOR (see above)
inputs_schema:
properties:
indicator:
type: string
description: The value to look up
required:
- indicator
outputs_schema:
properties: {}
request:
method: GET # GET | POST | PUT | PATCH | DELETE
path: /lookup/{indicator} # {indicator} is substituted from inputs
query: [] # input names to send as query params
body: [] # input names to send as JSON body fields
auth_ref: apikey
# --- Script-based command -------------------------------------------------
# No `request:` block. Provide scripts/<id>.py next to this manifest.
# The script receives inputs in the global dict __inputs__ and MUST print
# exactly one JSON object to stdout (that becomes the command output).
# See templates/scripts/example_command.py.
- id: example_command
name: Example scripted command
description: Demonstrates a Python-backed command.
risk: read
inputs_schema:
properties:
indicator:
type: string
required:
- indicator
outputs_schema:
properties: {}