In a Specter integration, a Link backend is the external risk engine Specter calls. Specter reaches it over
the
specter-v1 protocol — see Specter backends.Anatomy
id— a short identifier you choose, used to address the backend (for example when disambiguating an invocation).protocol— the protocol’s$idURL; must match an imported protocol.host— the provider’s hostname (no scheme, no path).credentials— consumed by the authentication pipeline; encrypted at rest.auth_pipeline— how outbound requests are authenticated (see below).connections— a map from action (oraction.variant) to its mapping configuration.
Authentication strategies
Theauth_pipeline determines how Link authenticates outbound calls to the provider. A non-empty pipeline
requires a source_type (inline or vault — where the credentials come from) and a credential_type:
credential_type | Behavior |
|---|---|
(omit the pipeline, or {}) | No authentication added. |
basic | HTTP Basic, from username / password credentials. |
bearer | Authorization: Bearer <token>. Set token_prefix to use a different scheme word (for example token). |
hmac_sha256 | HMAC-SHA256 request signing, including a Digest over the request body. |
Digest, etc.) take precedence over mapped headers on conflict, so signing
guarantees are never overwritten.
Credentials and secrets
Credentials are held either inline (encrypted at rest in the backend record) or pulled from a secrets store through a pointer, so the secret value itself never lives in the backend document — Link fetches it at request time. Inline credentials can be rotated in place with the rotate-credentials endpoint, without recreating the backend.Provisioning: self vs. managed
A backend declares a provisioning mode:self(default) — you own and manage the backend and its credentials.managed— the backend is provisioned and operated by Hellgate on your behalf (an optional service). Its create, update, and delete operations require the additionaladmin:managed-backends:writescope on top ofadmin:backends:write.
Mapping
Mapping is declarative: adapter logic lives in versioned templates, not in code. Each connection has arequest_mapping and a response_mapping.
Template grammar
Mapping leaves are plain JSON values. Strings carry a mustache-style grammar — zero or more{{ expr }}
holes inside a literal string — where expr is a scoped path followed by an optional chain of filters:
| Root | Resolves to | Valid in |
|---|---|---|
$req.body.* | The caller’s invoke request body | Request and response mappings |
$req.header.* | The caller’s request headers (downcased) | Request and response mappings |
$res.body.* | The provider’s response body | Response mappings only |
$res.header.* | The provider’s response headers | Response mappings only |
|. Common filters include required (abort the call if the value is missing),
default(<path or literal>) (fall back to another value), omit_if_null (drop the key when null),
map({ FROM: TO }) (translate enum values), to_int, and first / last / prefix for string slicing.
Request mapping
request_mapping mirrors the provider’s expected request. It has an optional body (a nested object matching
the provider’s shape) and headers (a flat map of header name to template):
Response mapping
response_mapping is keyed by the provider’s HTTP status (200, 4xx, default, …). Each entry has a
return (the HTTP status Link returns to the caller) and a body template that produces the protocol-shaped
result:
body templates are validated at backend-write time against the protocol’s response schema for the
given return code — missing required keys, unknown keys, and unmatched union branches are rejected before the
backend goes live.
Connection keys
A connection key is either the bare action name (assess) or action.variant (assess.pan) when the action
has a discriminator. Link validates connections against the imported protocol and rejects a backend that misses a
required action or variant.
Mocks
Instead of live mapping, a connection can declare mock responses — match conditions plus canned responses. When present, invocations are matched against the mocks and served without any external call. Mocks are ideal for local development, tests, and protocol fixtures.Message-level encryption
Some providers require every request and response body to be encrypted on top of TLS (JWE). A backend can carry anencryption block that makes Link encrypt outbound bodies and decrypt inbound ones transparently, so callers
keep sending and receiving plain JSON. Encryption keys are managed separately and support rotation.
Selecting a backend
When several enabled backends implement the same action and variant, the caller disambiguates with abackend
query parameter on invocation. See Invocation.
Next steps
Invocation
Call an action, match the method, select a backend, and handle errors.
Protocols
The contract a backend implements.