> ## Documentation Index
> Fetch the complete documentation index at: https://actelos.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Adapter Modules

> Modules that connect cyrnel to services and APIs

An **adapter module** is the part of cyrnel that knows how to talk to a
specific kind of end service. It is responsible for:

1. **Parsing definitions.** Turning whatever the user supplied at install
   time (an OpenAPI document, a gRPC proto, a custom JSON descriptor, …)
   into a `ServiceDefinition` cyrnel can store.
2. **Holding runtime state.** Storing per-service config and secrets, and
   any `adapterDomain` metadata produced at install time.
3. **Invoking tools.** Translating a generic
   `{ serviceId, toolId, parameters }` invocation into a real call to the
   underlying service.

Adapter modules implement the SDK [`AdapterModule`](/cyrnel/specs/adapter-module)
interface.

## Lifecycle

```ts theme={null}
interface AdapterModule extends Module {
  generateDefinition(input: string): Promise<ServiceDefinition>;
  hydrateService(state: ServiceState): Promise<void>;
  dehydrateService(id: string): Promise<void>;
  invoke(input: InvokeInput): Promise<unknown>;
}
```

1. **`setup({})`** is called once when the adapter is activated.
2. **`generateDefinition(content)`** is called during `POST /services`
   (direct install), `POST /services/install` (registry install),
   `PATCH /services/:serviceId` (direct update), and
   `POST /services/:serviceId/update` (registry update). Pure: given the
   same definition string, should produce the same `ServiceDefinition`.
3. **`hydrateService(state)`** is called whenever a service that targets this
   adapter is enabled, has its config or secrets patched, or this adapter is
   activated while the service is already enabled. Receives a snapshot:

   ```ts theme={null}
   interface ServiceState {
     id: string;
     adapterDomain: Record<string, unknown>;
     tools: Record<string, ToolState>;
     config: Record<string, unknown>;
     secrets: Record<string, unknown>;
   }
   ```

   The adapter is expected to store this and use it on subsequent
   `invoke` calls.
4. **`dehydrateService(id)`** is called when a service is disabled, deleted,
   or updated. The adapter should drop any state it kept for that service.
5. **`invoke(input)`** is called once per tool call. Must throw for an
   unknown `(serviceId, toolId)` pair or any transport-level failure.
6. **`teardown()`** is called when the adapter is deactivated. Should release
   any pooled resources.

## Security Model

Adapter modules are the **most security-sensitive** part of cyrnel. They:

* Receive **decrypted secrets** via `ServiceState.secrets`.
* Make **outbound network requests** (or open files, or send messages,
  etc.) to end services on behalf of users.
* Run in the **host Node.js process** with full host permissions, they are
  not sandboxed.

Concretely, the trust boundary is "the operator of the cyrnel server" decides
which adapters to enable. Every enabled adapter can:

* Read every secret of every service it owns.
* Reach anything reachable from the host.
* Persist or transmit anything it receives.

### Implications

* **Treat the contents of `$CYRNEL_DATA_DIR/modules/` as code with full host
  privileges.** Use the same review you would use for any other server
  code. A malicious adapter trivially exfiltrates secrets.
* **Don't enable adapters you can't read.** "Plug-and-play" is convenient
  but not safe.
* **Don't reuse `CYRNEL_SECRETS_KEY` across environments.** A leaked key,
  combined with a copy of `data.db`, decrypts every secret of every service.
* **Be careful with `adapterDomain`.** It is persisted in plaintext in the
  `services` table. If the adapter places URLs, header templates, or other
  identifying metadata there, treat that table as sensitive too.

### What an adapter must not do

* **Persist secrets outside `ServiceState`.** All secret retention must be
  in-memory; if the process restarts, `hydrateService` is called again.
  Writing them to disk silently breaks the encryption guarantee cyrnel is
  trying to give the operator.
* **Drop authentication on retry.** If a request fails, retry with the same
  credentials, not without them.
* **Hold per-service state in a way that survives `dehydrateService`.**

See [Security](/cyrnel/docs/security) for the broader picture.

## Authoring

See [Writing a custom adapter module](/cyrnel/specs/writing-custom-adapter-module)
for the file layout, a skeleton, and a full working example.
[`AdapterModule`](/cyrnel/specs/adapter-module) covers the interface contract
in full.
