Skip to main content
Version: 2.6

Connection Management

Operaide’s connection management module centralizes API keys, database credentials, and other integration secrets so that apps and reaktors can reference them by name instead of hard-coding secrets. Connections are organization-scoped assets that pair a connection type (for example firecrawl or jina) with a validated configuration object.

Why Connections Exist

  • Separation of concerns – Reaktors read credentials through registerConnectionType().getConnection(name) while connection owners control the actual secret values in one place.
  • Schema-backed validation – Each connection type ships a Zod schema. The UI uses the schema to render a form and validate input before storing it. Runtime code also re-validates the stored configuration to avoid executing with stale or malformed data.
  • Operational safety – Permissions, audit metadata, and warnings inside the UI make it obvious who owns a connection, when it was updated, and whether it still matches the provider’s schema.

When a Connection Schema Changes

Connection providers can evolve their Zod schema, for example by adding a new optional field. The server automatically reconciles stored connections:

  1. Each time the UI lists connections or a provider calls getConnection() the service looks up the current schema from the global registry (globalConnectionRegistry.getType).
  2. Stored configurations are validated against the new schema. Incompatible data raises a warning in the UI and throws a runtime error when a reaktor tries to use the connection.
  3. When validation succeeds, the service regenerates the stored JSON schema and UI schema (updateConnectionSchemaIfNeeded) so the admin form reflects the latest provider definition without manual migrations.

This means providers can roll out backwards-compatible schema changes with minimal friction, while breaking changes surface immediately and safely.

If the Providing App Is Removed

Connection definitions live inside the app that registers them. When that app is uninstalled or disabled:

  • The connection document remains in the database, but the registry no longer contains the connection type. The UI flags the connection as "not registered" and falls back to the stored schema so existing data stays editable.
  • Runtime calls to registerConnectionType(...).getConnection() from that removed app will obviously stop working because the registration code is gone. Other apps that still depend on the connection should either re-install the provider app or supply their own registration.
  • Because the stored configuration is preserved, re-installing the app automatically reattaches the schema and the warning disappears.

Creating Your Own Connection Type

Any app or extension can publish a new provider by calling registerConnectionType on startup:

Example: Jina Markdown connection
import { z } from 'zod';
import { registerConnectionType } from '@operaide/aktor';

const JinaConfig = z.object({
apiKey: z.string().min(1),
baseUrl: z.string().url().default('https://r.jina.ai/'),
});

export const jinaConnectionType = registerConnectionType({
type: 'jina',
label: 'Jina API',
configSchema: JinaConfig,
credentialPortalUrl: 'https://cloud.jina.ai/',
descriptionMarkdown: `Use this connection to fetch markdown via **r.jina.ai**.`,
});

Key points:

  1. Register on the server – run the registration from your app’s server entry point (for example in the extension’s extension.ts).
  2. Provide a Zod schema – the schema powers validation and renders the configuration form automatically.
  3. Use metadata for better UX – optional fields such as credentialPortalUrl, providerIconUrl, and descriptionMarkdown enrich the UI with icons, setup instructions, and quick-links. If you omit the icon, Operaide will automatically fall back to the favicon of the credential portal URL.
  4. Consume the connection – wherever you need credentials, call jinaConnectionType.getConnection('connectionName'). The service validates and returns the typed configuration so your reaktor can use it.

By following this pattern every app can offer reusable, schema-checked connections that feel native inside the Operaide admin experience.

Using a Connection Type Inside Your App

Once a provider is registered you typically need two pieces of code: a UI setting so operators can pick the connection name, and the place in your reaktor or service that resolves the connection at runtime.

1. Allow operators to pick a connection name inside your reaktor

Settings must be declared in the factory function you pass to registerReaktorDefinition (or within a nested aktor). The following snippet shows a minimal reaktor that exposes a connection-name setting and forwards the request to an aktor.

Expose a connection setting inside a reaktor
import { registerReaktorDefinition, aktorSetting, defineAktor } from '@operaide/aktor';
import { z } from 'zod';
import { jinaConnectionType } from './JinaConnection';

const aktorFetchMarkdown = defineAktor('aktorFetchMarkdown', {
async createAktor({ url, connectionName }) {
const connection = await jinaConnectionType.getConnection(connectionName);
const response = await fetch(`${connection.baseUrl}${encodeURIComponent(url)}`, {
headers: { Authorization: `Bearer ${connection.apiKey}` },
});
return response.text();
},
});

registerReaktorDefinition({
reaktorDefinitionId: 'markdown-from-jina',
label: 'Fetch Markdown via Jina',
description: 'Fetches markdown for a given URL through a managed connection.',
createReaktor({ url }) {
const connectionName = aktorSetting(
z.string().min(1),
'default',
'Jina connection name'
);

return aktorFetchMarkdown({ url, connectionName });
},
inputSchema: z.object({ url: z.string().url() }),
outputSchema: z.string(),
});

This renders a setting in the reaktor configuration UI so operators can enter the name of a connection they created under Platform → Connections.

If the stored configuration ever stops matching the provider schema, the jinaConnectionType.getConnection() call will throw a descriptive error, allowing the reaktor to fail fast and highlight the mismatch.

2. Create the connection in the UI

  1. Navigate to Platform → Connections.
  2. Click Add Connection, select the desired type, and fill in the schema-backed form.
  3. Save the connection and reuse its name anywhere you configured aktorSetting (step 1).

With these pieces in place your reaktor treats credentials as declarative infrastructure and you don’t have to hard-code secrets in source control.