Adapters (Substrate Reference)
This page is the reference for consuming the substrate adapters that ship
with Assembly Line. To discover and install plugins — including the full catalog
and assembly-line add — start at Plugins.
Assembly Line adapters are deliberately small. gateway.ts chooses where the
runtime runs and which durable services it uses; channel files choose how
external events become Assembly Line turns; sandbox files choose where isolated code
and shell work runs. These choices are independent.
Adapters are substitutable substrate: each role (channel, sandbox, blob, state,
scheduler, gateway/deploy) has a generic contract with interchangeable
providers, so the same agent runs unchanged on different infrastructure.
Capabilities are a separate tier. A capability is a single-vendor feature that
gives an agent something new to do rather than somewhere new to run — it exposes
that vendor's own surface and has no generic contract to swap behind. Capability
packages live under capabilities/ (see Capabilities), still
ride the same open provider seam, and describe themselves through provider
metadata so tools like the no-code builder can scaffold them automatically.
Adapter support levels:
- Supported means Assembly Line has a real runtime instantiation path, docs, and regression coverage in this repo.
- Preview means the public helper, compiler metadata, and local regression coverage exist, but the adapter still needs live provider smoke coverage or more provider hardening before it should be announced as fully supported.
- Planned means the docs may name the direction, but the adapter is not a production claim yet.
The current matrix is:
| Role | Supported | Preview | Planned |
|---|---|---|---|
| Channels | Slack, Discord, Telegram, Microsoft Teams, Photon/Spectrum | - | - |
| Sandboxes | local dev/test, Docker, Daytona, E2B | Modal | - |
| Blob storage | local dev/test, R2, generic S3-compatible storage | - | - |
| Durable state | local dev/test files, Postgres for production, with Neon, Railway, Supabase, local, and custom presets | - | other database families |
| Scheduler | local in-process loop, gateway-triggered cloud scheduler, Postgres-backed multi-worker loop | - | - |
| Gateway/deploy | local, Railway | Docker, Fly, generic VPS | provider-managed VPS provisioning and other gateway families |
Other database families, gateway families, and sandbox providers are not part of this initial adapter set.
Primary model engines are not an adapter role in this matrix. Pi remains the
default. Separately, the Node host includes a preview openai-codex/*
provider-prefix route backed by the official Codex app-server. That route is
implemented in @assemblyline-agents/codex but is not provider-package registration and
does not add a public harness: field to agent.ts.
Capabilities are tracked separately from the adapter roles above because they are not substitutable substrate:
| Capability | Supported | Preview | Planned |
|---|---|---|---|
| Voice / telephony | - | LiveKit voice dispatch and SIP calls | - |
Provider Docs Used
The adapter shapes follow current provider docs for:
- Discord interactions
- Discord Gateway
- Slack agents
- Slack Events API
- Slack request verification
- Telegram Bot API
- GitHub App authentication
- Microsoft Bot Connector authentication
- LiveKit Agents telephony
- LiveKit agent dispatch
- LiveKit SIP API
- LiveKit access tokens
- Docker containers
- Docker resource constraints
- Daytona sandboxes
- E2B sandboxes
- Modal sandboxes
- Modal sandbox files
- Modal sandbox snapshots
- R2 S3 compatibility
- Amazon S3 API
- Neon Postgres connections
- Supabase Postgres connections
- Railway CLI
- Fly deploy
- OpenSSH client
Gateway
Gateway adapters answer one question: where does the compiled Assembly Line runtime service run?
They do not choose your state, blob, sandbox, channels, or connections. Those
are separate settings in the same gateway.ts file.
import { adapter, defineGateway } from "@assemblyline-agents/core";
import { neonPostgres } from "@assemblyline-agents/postgres";
import { r2Blob } from "@assemblyline-agents/s3";
import { dockerSandbox } from "@assemblyline-agents/docker";
export default defineGateway({
deploy: adapter("railway"),
runtime: adapter("node"),
state: neonPostgres(),
blob: r2Blob(),
sandbox: dockerSandbox()
});
Deploy target status:
- Supported:
adapter("railway")orrailwayDeploy()runs the hosted Node runtime through the@assemblyline-agents/railwaydeploy publisher. - Supported:
adapter("docker")ordockerDeploy()builds the compiled artifact as a Docker image through the@assemblyline-agents/dockerdeploy publisher and can optionally run it locally. - Supported:
adapter("fly")orflyDeploy()generatesfly.tomland publishes the artifact through the@assemblyline-agents/flydeploy publisher andflyctl deploy. - Supported:
adapter("vps")orvpsDeploy()deploys to a named AMD64 Ubuntu 24.04/26.04 or Debian 12 host over fingerprint-pinned SSH. It manages per-agent containers, networks, storage, database access, secrets, Caddy routing, and transactional blue/green releases without mounting the Docker socket into an agent. Hetzner hosts can also be securely created or adopted throughassembly-line hosts bootstrap.
Useful CLI flags:
assembly-line deploy --target docker --docker-image assembly-line/my-agent --serve
assembly-line deploy --target fly --fly-app my-agent --fly-region iad
assembly-line deploy --target railway --railway-project prj_x --railway-service svc_y
assembly-line deploy --target vps --vps-host production-eu --vps-domain agent.example.com --sync-secrets
Community Plugin Providers
Any npm package can supply a state, blob, sandbox, or deploy provider that
agents select with adapter(kind, options, { package }) — see
Authoring Plugins for the contract, preflight, and
artifact-packaging behavior.
Scheduler
Schedules compile to registration metadata, but the scheduler adapter chooses where the clock lives.
import { adapter, defineGateway } from "@assemblyline-agents/core";
export default defineGateway({
scheduler: adapter("gateway")
});
Supported scheduler adapters:
adapter("local"): starts an in-process polling loop with the Node host. Use this for local development, tests, and simple single-process hosts.adapter("gateway"): does not start a local loop. Use a platform cron, cloud scheduler, Durable Object alarm, queue worker, or gateway route to callGETorPOST /assembly-line/automations/tick, or callruntime.runDueAutomations()from host code.adapter("postgres"): starts the polling loop and coordinates duplicate workers through the Postgres state adapter's idempotency and dynamic schedule leases. Pair it withstate: adapter("postgres").
For gateway-triggered production schedulers, set ASSEMBLY_LINE_SCHEDULER_SECRET
and send it as Authorization: Bearer <secret> or
x-assembly-line-scheduler-secret.
Capabilities
Capabilities live under capabilities/ and are single-vendor features, not
substitutable adapters. They expose focused clients, tools, definitions, and
connection metadata instead of pretending to implement an interchangeable
runtime role. A no-code builder or catalog can use that metadata to render a
plugin card and credential form without making the capability an agent engine.
Subagents And Model Routing
Every primary agent and child agent runs through Pi. Subagents are isolation and delegation boundaries, not engine adapters; their definitions may narrow the model, workspace, tools, and connections, while omitted models inherit the primary model.
@assemblyline-agents/codex also exports codexAgentHarness() for the Node host's preview
primary-model route. It exposes Assembly Line tools to app-server as dynamic tools,
keeps execution and approvals in the Assembly Line runtime, and persists the Codex
thread id as opaque continuation state. Authentication stays inside the Codex
CLI session. App-server reports whether that session is using ChatGPT or an
API key; Assembly Line records the billing rail and provider token/credit snapshots
without reading either credential. For Railway deployments, selecting an
openai-codex/* primary model packages the official CLI, provisions persistent
/data/codex storage, and supports direct remote device authentication through
assembly-line auth codex; no OAuth token is copied into Railway variables.
The Pi/OpenRouter route records native token and charged-credit receipts, with generation-ID reconciliation when settlement is delayed. Codex reports per-turn tokens but not per-turn dollars; API-key cash is therefore reconciled as an OpenAI organization control total and is attributed to an agent only when an operator provides a dedicated project or API-key mapping.
LiveKit Voice
Assembly Line does not run realtime media inside the text-model runtime. Instead,
@assemblyline-agents/livekit signs LiveKit server tokens,
calls the LiveKit Agent Dispatch and SIP Twirp APIs, and lets an Assembly Line turn
start or route a LiveKit voice session.
Use a tool when the parent agent should decide to dial:
// tools/start_call.ts
import { defineLiveKitOutboundCallTool } from "@assemblyline-agents/livekit";
export default defineLiveKitOutboundCallTool({
agentName: "support-voice",
outboundTrunkId: "ST_outbound"
});
Declare the connection when the agent or a Pi-backed subagent should receive LiveKit credentials and preflight requirements:
// connections/livekit.ts
import { defineLiveKitConnection } from "@assemblyline-agents/livekit";
export default defineLiveKitConnection();
The LiveKit agent named by agentName must be running in your LiveKit Agents
worker. For outbound phone calls, Assembly Line dispatches that worker into a room
and calls CreateSIPParticipant to dial the callee through your outbound SIP
trunk. Incoming phone calls should be routed in LiveKit with SIP dispatch
rules to the LiveKit agent worker; Assembly Line can still be used by that worker as
an app/runtime layer, but the phone media path remains LiveKit.
This keeps the boundary explicit: LiveKit owns rooms, media, dispatch, and SIP; Assembly Line owns Pi reasoning, typed tool calls, and durable run state.
Required environment:
| Purpose | Env |
|---|---|
| LiveKit server API | LIVEKIT_URL, LIVEKIT_API_KEY, LIVEKIT_API_SECRET |
| Default outbound dialing | optional LIVEKIT_OUTBOUND_TRUNK_ID |
| Default voice worker dispatch | optional LIVEKIT_VOICE_AGENT_NAME |
Channels
Channel packages export one-line helpers for normal use. They verify provider
auth, normalize the provider event into ChannelTurn, preserve provider IDs in
metadata/delivery, and send the final reply through provider APIs.
After a deploy, point each channel's provider at the deployed ingress URL with:
assembly-line channels wire <agentRoot> --url https://your-service.example.com
It computes each channel's ingress URL from the compiled route table and, for
Telegram, calls setWebhook directly (needs TELEGRAM_BOT_TOKEN, and uses
TELEGRAM_WEBHOOK_SECRET when set). For providers that configure their endpoint
in a console (Slack Request URL, Discord Interactions Endpoint, Teams messaging
endpoint), it prints the exact URL to paste. Output is a structured
ChannelWireResult[] (action: "set" | "manual").
// channels/slack.ts
import { defineSlackChannel } from "@assemblyline-agents/slack";
export default defineSlackChannel();
// channels/discord.ts
import { defineDiscordChannel } from "@assemblyline-agents/discord";
export default defineDiscordChannel({
gateway: true,
requireMention: true
});
// channels/telegram.ts
import { defineTelegramChannel } from "@assemblyline-agents/telegram";
export default defineTelegramChannel();
// channels/teams.ts
import { defineTeamsChannel } from "@assemblyline-agents/teams";
export default defineTeamsChannel();
Required channel environment:
| Channel | Required env |
|---|---|
| Slack | SLACK_SIGNING_SECRET, SLACK_BOT_TOKEN, optional SLACK_BOT_USER_ID, optional SLACK_ASSISTANT_ENABLED |
| Discord | DISCORD_PUBLIC_KEY, DISCORD_APPLICATION_ID, DISCORD_BOT_TOKEN, optional DISCORD_GATEWAY_ENABLED, optional DISCORD_GATEWAY_INTENTS, optional DISCORD_BOT_USER_ID |
| Telegram | TELEGRAM_BOT_TOKEN; TELEGRAM_WEBHOOK_SECRET is required outside devMode |
| Teams | MICROSOFT_APP_ID, MICROSOFT_APP_PASSWORD, optional ASSEMBLY_LINE_TEAMS_ALLOWED_TENANTS, optional ASSEMBLY_LINE_TEAMS_ALLOWED_SERVICE_URLS |
Channel-owned ingress auth and attachment resolution
Channels declare their own production ingress-auth requirements and resolve their own provider attachments; the runtime stays a generic dispatcher.
ChannelDefinition.ingress.requiredSecretEnvis a list of any-of groups of env var names: production boot succeeds when every var in at least one group is set (for example Photon declares[["PHOTON_WEBHOOK_SIGNING_SECRET"], ["PHOTON_INGRESS_TOKEN"]]). The compiler stamps the declaration intoCompiledChannel.metadata.ingress; in production the runtime refuses to boot when no group is satisfied, and indevModeit logs a warning instead. The built-in helpers (defineSlackChannel,defineTelegramChannel,defineDiscordChannel,defineTeamsChannel,definePhotonChannel) declare this automatically; custom channels can setingresson their channel config and re-export the same shape asingressAuthon the module.ChannelModule.resolveAttachment(attachment, ctx)turns a turn attachment into a download request ({ url, headers, filename? }). The runtime performs the download, applies size/type limits and timeouts, and stores the blob; the channel owns auth lookups (Slackfiles.info, TelegramgetFile, the Teams Bot Framework token flow, Photon bridge bearer headers) and host allowlists. Returnundefinedto fall back to a generic unauthenticated URL download.- Trust boundary: the runtime only calls
resolveAttachmenton the module of the channel that produced the turn, and only when the turn's declared provider matches that channel — a hostile attachment claiming another provider can never route to that provider's credentials. Inside a resolver, useattachmentTrustedForProvider(attachment, "<provider>")andattachmentRemoteUrl(attachment)from@assemblyline-agents/runtimeto honor the per-attachmentremote.trustedmarkers before attaching credentials.
Attachment materialization
Inbound files follow the same normalized ChannelTurn.attachments contract on
every channel. Adapters should preserve provider file identity plus one of:
- inline content (
content,text,body, or base64data) - a safe remote reference (
url,downloadUrl,contentUrl, orremote: { provider, auth?, url }) - provider-specific lookup metadata such as Telegram
file_id
The runtime materializes remote attachments after the webhook ACK, stores the
original bytes through the configured blob adapter, records them as read-only
/files/original/... resources, and lists them in /files/manifest.json.
Private download details and auth hints are stripped from model-visible
attachment context after storage. Text and Markdown attachments read back as
UTF-8; binary files remain byte-accurate for resource projection. ZIP uploads
are also expanded when possible: the original archive remains available under
/files/original/..., and safe entries are exposed as read-only resources under
/files/extracted/<archive-name>/... for the core file tools. Unsupported, encrypted, oversized, or path-escaping ZIP
entries are skipped or reported without dropping the original uploaded archive.
Stored PNG, JPEG, GIF, WebP, MP4, MPEG, MOV, and WebM attachments are
additionally supplied to the primary harness as typed model media. Adapters
should therefore preserve the correct MIME type instead of labeling every
upload as application/octet-stream. The runtime never exposes private
attachment URLs to the model; it reads the configured blob and gives the
harness bounded base64 bytes. A harness sends complete video only when the
selected model advertises native video support. Otherwise Assembly Line reports the
limitation and does not sample frames unless the user explicitly requests that
approximation.
Slack uses the Events API route /slack/events. It verifies the raw request
body with Slack's signing secret, handles url_verification inline, returns a
2xx ACK for accepted events before model work, and uses Slack event_id as the
delivery idempotency key. It starts turns only for intentional agent entry
points: app mentions, user DM messages, and assistant-thread user messages when
assistant mode is enabled. Delivery always uses the preserved Slack channel and
thread target from the normalized turn.
Slack context augmentation runs after ACK and before default context bundle construction. It supplies the current conversation history plus up to three recent Assembly Line Slack conversations for the same user from the last 24 hours as short summaries and structured metadata. It does not fetch workspace-wide Slack history during ingress.
Agent communication channel status:
- Slack and Photon/Spectrum are reference agent channels. Slack maps app mentions, DMs, and assistant-thread user messages into durable Assembly Line turns; Photon/Spectrum maps iMessage bridge events into the same lifecycle and adds native actions such as reactions, polls, app cards, and backgrounds.
- Discord is an agent-oriented communication channel when Gateway ingress is enabled. Interactions still cover slash commands, components, modals, and autocomplete; Gateway ingress covers DMs, mentions, and thread/channel messages. Discord starts typing indicators for Gateway turns and delivers through either interaction responses or bot-token channel messages.
- Telegram is an agent-oriented communication channel over Bot API webhooks. It
supports messages, edited messages, callback queries, channel posts, business
messages, forum topics, typing actions, inline keyboards, callback answers,
media sends, and
getFileattachment materialization. - Microsoft Teams is an agent-oriented communication channel over Bot Framework activities. It supports message activities, Adaptive Card invoke submissions, mention stripping, tenant/service URL constraints, typing activities, Adaptive Card replies, suggested actions, and protected attachment materialization.
Connections
Connection helpers remain available when agents need provider capabilities beyond receiving messages. Channels own inbound events and reply delivery; connections own typed provider capabilities, credential requirements, and remote tool discovery.
GitHub is connection-only: use it for repository, issue, pull-request, and workflow tools rather than as an inbound communication channel.
// connections/github.ts
import { defineGitHubConnection } from "@assemblyline-agents/github";
export default defineGitHubConnection();
// connections/teams.ts
import { defineTeamsConnection } from "@assemblyline-agents/teams";
export default defineTeamsConnection();
GitHub App connections require GITHUB_APP_ID and GITHUB_PRIVATE_KEY. Teams
uses Bot Framework credentials.
Live smoke evidence for Discord, Telegram, and Teams is written locally under
docs/internal/evidence/channels/ (a gitignored working directory). The root smoke scripts skip without
credentials and point to those provider checklists when credentials are present.
Sandboxes
The sandbox adapter is acquired lazily when a tool or capability asks for a sandbox. Normal channel receipt, model turns without sandbox-backed tools, skill activation, memory reads/writes, and final delivery do not need to pay sandbox startup cost.
// sandbox/default.ts
import { dockerSandbox } from "@assemblyline-agents/docker";
export default dockerSandbox({
image: "node:22-slim",
network: "none"
});
Sandbox adapter status:
- Supported:
adapter("local")for trusted dev/test only. - Supported:
adapter("docker")ordockerSandbox()for local container isolation, one container per acquired session, cleanup on dispose, and a physical/workspacecontainer cwd. - Supported:
adapter("daytona")for hosted Daytona sandboxes. - Supported:
adapter("e2b")ore2bSandbox()for hosted E2B sandboxes. - Supported:
adapter("modal")ormodalSandbox()for hosted Modal sandboxes. Its JavaScript SDK, filesystem, lifecycle, readiness probe, image, tag, and snapshot bindings are compile-checked against the installed Modal SDK.
All hosted built-in sandbox adapters expose the same physical namespace:
shells start in /workspace, absolute /workspace/... shell paths and provider
file APIs address the same files, and create/connect/wake fail if that invariant
does not hold. Docker uses its native container workdir, Daytona builds the
configured image with WORKDIR /workspace, Modal extends its image and passes
the native Sandbox workdir, and E2B idempotently provisions /workspace
through its root command facility before returning to the template's ordinary
command user. .. traversal is rejected. Recursive listings return canonical
absolute paths and include contents for runtime persistence.
The filesystem contract is versioned in provider metadata, labels/tags,
provider-safe names, runtime manifests, and sync jobs. A runtime never
reconnects or restores a snapshot from an obsolete contract. The Local adapter
is explicitly a trusted dev/test logical emulation over a host temporary
directory; Docker is the local conformance path when physical /workspace
semantics matter.
Dirty sandbox sessions are retained when async sandbox sync is pending:
Docker stops the container, Daytona pauses/stops, and E2B pauses with configurable
memory retention. Modal detaches retained sessions and terminates clean ones.
Clean sessions are removed,
deleted, killed, or terminated through the provider lifecycle API. The sync worker
reconnects first and then calls wake()/start when a retained sandbox is warm
or paused.
Hosted adapters do not silently fall back to local execution. Daytona local
fallback exists only for explicit development/test opt-in with
ASSEMBLY_LINE_ALLOW_LOCAL_SANDBOX_FALLBACK=true.
The local sandbox is for trusted dev/test execution. Production Node runtime
construction rejects adapter("local") for sandboxes unless
ASSEMBLY_LINE_ALLOW_LOCAL_SANDBOX_IN_PRODUCTION=true is set to acknowledge that the
host process, filesystem, and network are not isolated. Docker defaults
ASSEMBLY_LINE_DOCKER_NETWORK to none; Daytona supports
ASSEMBLY_LINE_DAYTONA_NETWORK_BLOCK_ALL and allow/domain lists for explicit egress
policy.
Provider env:
| Sandbox | Required env | Lifecycle and policy env |
|---|---|---|
| Docker | Docker CLI/daemon available | Optional DOCKER_HOST, ASSEMBLY_LINE_DOCKER_NETWORK (defaults to none), ASSEMBLY_LINE_DOCKER_CPUS, ASSEMBLY_LINE_DOCKER_MEMORY, ASSEMBLY_LINE_DOCKER_PULL_POLICY, ASSEMBLY_LINE_DOCKER_COMMAND_TIMEOUT_MS |
| Daytona | DAYTONA_API_KEY | Optional DAYTONA_API_URL, DAYTONA_TARGET, ASSEMBLY_LINE_DAYTONA_CREATE_TIMEOUT_SECONDS, ASSEMBLY_LINE_DAYTONA_LIFECYCLE_TIMEOUT_SECONDS, ASSEMBLY_LINE_DAYTONA_AUTO_STOP_MINUTES, ASSEMBLY_LINE_DAYTONA_AUTO_ARCHIVE_MINUTES, ASSEMBLY_LINE_DAYTONA_AUTO_DELETE_MINUTES, ASSEMBLY_LINE_DAYTONA_EPHEMERAL, ASSEMBLY_LINE_DAYTONA_NETWORK_BLOCK_ALL, ASSEMBLY_LINE_DAYTONA_NETWORK_ALLOW_LIST, ASSEMBLY_LINE_DAYTONA_DOMAIN_ALLOW_LIST |
| E2B | E2B_API_KEY | Optional E2B_TEMPLATE, ASSEMBLY_LINE_E2B_TIMEOUT_MS, ASSEMBLY_LINE_E2B_RETAIN_TIMEOUT_MS, ASSEMBLY_LINE_E2B_REQUEST_TIMEOUT_MS, ASSEMBLY_LINE_E2B_PAUSE_KEEP_MEMORY, ASSEMBLY_LINE_E2B_ALLOW_INTERNET_ACCESS |
| Modal | MODAL_TOKEN_ID, MODAL_TOKEN_SECRET | Optional MODAL_APP_NAME, ASSEMBLY_LINE_MODAL_TIMEOUT_MS, ASSEMBLY_LINE_MODAL_WAIT_READY |
Provider snapshots are opt-in through the Assembly Line sandbox snapshot policy. Docker reports snapshots as unsupported. Daytona and E2B expose snapshot creation only when the installed provider SDK exposes it; Modal uses its filesystem snapshot image API. Normal production persistence remains Assembly Line state/blob sync.
Live sandbox smoke is opt-in:
pnpm smoke:sandbox -- --provider=docker
pnpm smoke:sandbox -- --provider=daytona
pnpm smoke:sandbox -- --provider=e2b
pnpm smoke:sandbox -- --provider=modal
Docker smoke runs when Docker is available. Hosted smoke skips cleanly when
disposable provider credentials are missing. Smoke evidence is written locally under
docs/internal/smoke/ (gitignored) and records provider, lifecycle result, safe file hashes,
and snapshot status without secrets.
Deploy parity checks are also available without hosted-provider usage:
pnpm smoke:deploy:docker
pnpm smoke:deploy:fly
pnpm smoke:deploy:fly:live
The Docker check performs a real local image build, container recreation,
remote command, and persistent-volume lifecycle. The Fly check gives the
generated fly.toml to the installed flyctl local parser and verifies the
deploy, volume, Machine, and SSH flags used by the publisher. It uses no valid
Fly credential and cannot create provider resources. The explicit :live
variant creates one ephemeral Fly app and volume, tests a real deploy, HTTP
health, secret sync, SSH, redeploy persistence, and publisher-owned teardown,
then verifies the app is absent. Hosted Fly and Modal smoke require credentials
and may incur provider usage.
Blob Storage
Production blob storage is S3-compatible. R2 remains first-class through the R2 wrapper, but the runtime contract is the same for R2, AWS S3, and MinIO-style endpoints.
import { defineGateway } from "@assemblyline-agents/core";
import { s3Blob, r2Blob, minioBlob } from "@assemblyline-agents/s3";
export default defineGateway({
blob: r2Blob()
// or blob: s3Blob()
// or blob: minioBlob()
});
Generic S3 env:
S3_BUCKETS3_REGIONS3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEY- optional
S3_ENDPOINT - optional
S3_FORCE_PATH_STYLE - optional
S3_PREFIX - optional
S3_PUBLIC_BASE_URL
R2 env:
R2_ACCOUNT_IDR2_BUCKETR2_ACCESS_KEY_IDR2_SECRET_ACCESS_KEY- optional
R2_PREFIX - optional
R2_PUBLIC_BASE_URL
@assemblyline-agents/r2 still exports r2Adapter(), R2BlobAdapter, and
InMemoryR2Bucket for existing imports.
S3_PUBLIC_BASE_URL and R2_PUBLIC_BASE_URL do not make every blob public.
Blob writes are private by default; adapters return public HTTP URLs only when
the write explicitly uses { visibility: "public" }.
Database
Assembly Line stays opinionated here: Postgres is the only production durable state plane in this phase. That keeps runs, messages, tool traces, schedules, dynamic connections, durable skills, idempotency, and migrations on one auditable database contract.
SQLite is not included because it would create a second production state shape
just as deploy targets and channels are expanding. File-backed state remains for
local dev/demo/test, not hosted production: it is single-process by design (its
idempotency reservations live in process memory, so two hosts sharing one state
file cannot coordinate leases or claims). Its writes are crash-safe — temp file,
fsync, then atomic rename — and an unreadable state file is backed up to
<path>.corrupt-<timestamp> and replaced with a fresh state instead of
crashing the host, but multi-replica guarantees always require Postgres.
Connection grant and authorization-session stores follow the same production
rule. Postgres implements those stores directly. File-backed connection
credential stores are for local development or deliberately small deployments;
production Node hosts using them must set ASSEMBLY_LINE_CONNECTION_STORE_SECRET or
ASSEMBLY_LINE_SECRET to a stable secret of at least 32 characters. The known local
development fallback is rejected outside devMode: true.
import { defineGateway } from "@assemblyline-agents/core";
import {
neonPostgres,
railwayPostgres,
supabasePostgres,
localPostgres,
postgresAdapter
} from "@assemblyline-agents/postgres";
export default defineGateway({
state: neonPostgres()
// or state: railwayPostgres()
// or state: supabasePostgres()
// or state: localPostgres()
// or state: postgresAdapter({ provider: "custom" })
});
Default env:
DATABASE_URL- optional
ASSEMBLY_LINE_POSTGRES_CONNECTION_ENV - optional
ASSEMBLY_LINE_POSTGRES_SSL_REJECT_UNAUTHORIZED(defaults totrue; setfalsefor self-signed/proxied certs)
Neon, Railway, Supabase, local Postgres, and custom Postgres all run the same
Assembly Line migrations and schema. On a Railway deploy, railwayPostgres() uses the
Railway CLI to reuse the configured database service or provision a real
Postgres service, then sets the application service's DATABASE_URL to a
private Railway reference variable before publishing. Its defaults are
{ databaseService: "Postgres", provision: true }; set provision: false to
select a named pre-existing service instead. Automatic creation uses Railway's
default Postgres service name. Railway's official Postgres image uses a
generated certificate, so this preset keeps TLS enabled but defaults
sslRejectUnauthorized to false. For Supabase, copy a direct connection
string for a long-lived IPv6-capable host, or a session-pooler connection string
when the host requires IPv4. Store either one as DATABASE_URL.
Observability
Telemetry is not a gateway adapter — it lives in agent/instrumentation.ts.
@assemblyline-agents/otlp provides an OTLP/HTTP GenAI telemetry sink you wire in the
setup callback; see
Customizing Agents → Observability for the
setup, capture-detail (captureContent) options, and the Langfuse recipe.