Ch9. Channels, Auth, Streaming
How Eve channels own session creation, continuation tokens, route auth, NDJSON streams, and frontend/client integration.
Written and reviewed by Eric Han · Co-founder & CTO, reopt
Key takeaways
- A channel is the agent's front door: auth, input normalization, session cursor ownership, and delivery.
- Route auth fails closed, but session ownership ACLs are still the application/channel's responsibility.
- A continuation token is not a queue; ordering and burst handling belong in the channel/app layer.
If a channel is wrong, model quality and tool safety cannot save the system. Channels decide who can enter, which session they resume, and where results or human prompts are delivered.
Channel Responsibilities
| Responsibility | Description |
|---|---|
| input normalization | turn Slack, HTTP, GitHub, etc. into user messages |
| continuation ownership | map a thread/conversation/job to a continuation token |
| delivery | send responses, failures, approvals, and auth prompts |
The default Eve HTTP channel exposes POST /eve/v1/session, POST /eve/v1/session/:sessionId, and GET /eve/v1/session/:sessionId/stream.
Default Eve HTTP Channel
If you do not author agent/channels/eve.ts, Eve provides a framework default. You usually author the file to customize route auth.
import { eveChannel } from "eve/channels/eve";
import { localDev, vercelOidc } from "eve/channels/auth";
export default eveChannel({
auth: [localDev(), vercelOidc()],
});Official Auth & Route Protection distinguishes framework default from scaffolded auth:
| Case | Auth | Meaning |
|---|---|---|
no authored eve.ts | [localDev(), vercelOidc()] | localhost and Vercel OIDC callers only |
eve init scaffold | [localDev(), vercelOidc(), placeholderAuth()] | structured production 401 until real auth is configured |
placeholderAuth() is a guardrail, not a production auth policy.
Route Auth Fails Closed
| AuthFn result | Meaning |
|---|---|
returns SessionAuthContext | success, stop the walk |
returns null/undefined | continue to next AuthFn |
| throws | reject with 401/403 |
| all skip | reject with 401 |
Anonymous access requires explicit none().
Route Auth Is Not Session Ownership
If multiple users can call the same Eve route, you still need a session ownership check.
Danger:
any signed-in user -> Eve route
knows sessionId or continuationToken
can follow up or stream another tenant sessionRecommended structure:
| Layer | Standard |
|---|---|
| route auth | authenticate user or service |
| session mapping | store sessionId -> owner/tenant/channel |
| follow-up guard | principal must own or participate in session |
| stream guard | stream endpoint enforces the same ownership |
Continuation Token Is Not A Queue
In 0.82.0, messages default to turnPolicy: "steer"; use "queue" to wait for the active turn to end. Steering messages from the current caller retain durable arrival order and may be combined at a boundary. Other callers' messages wait for the turn to end.
Use structured inputResponses with requestId to answer approvals unambiguously. A current caller's ordinary message that matches no approval option cancels that pending approval and steers, even with queue policy. Already-approved calls in the same batch may still run. turn.waiting does not end the turn; keep consuming the stream.
Validate x-eve-stream-version and reject unsupported contracts. Use the current client's normalization rather than combining older append events with current deltas manually.
Sessions, runs, and streaming.
NDJSON Stream
GET /eve/v1/session/:sessionId/stream returns newline-delimited JSON events.
| Event | Meaning |
|---|---|
session.started | durable session created |
turn.started | user turn started |
step.started | model step started |
actions.requested | tool/subagent calls requested |
action.result | action result |
input.requested | approval, question, or OAuth needed |
subagent.called | child session available |
message.appended | assistant text delta |
message.completed | assistant message completed |
result.completed | structured output completed |
step.failed/turn.failed/session.failed | failure boundary |
turn.waiting | Open turn waiting for approval, input, or authentication |
session.waiting | waiting for next input |
Reasoning events and tool results may need privacy and retention controls.
Event Dispatch Order
- Channel handler runs and updates adapter state.
- Stream event is durably recorded.
- Hooks run.
- Dynamic tool/skill/instruction resolvers run.
This lets dynamic resolvers depend on channel metadata projected by channel handlers.
Custom Channels
import { defineChannel, POST } from "eve/channels";
export default defineChannel({
routes: [
POST("/incident", async (req, args) => {
const incident = await req.json();
args.waitUntil(
args.send(`Investigate incident ${incident.id}`, {
auth: {
authenticator: "incident-service",
principalType: "service",
principalId: incident.actorId,
attributes: { incidentId: incident.id },
},
continuationToken: `incident:${incident.id}`,
}),
);
return new Response("ok");
}),
],
});Review:
- HMAC/JWT/OIDC raw request authentication
- never trust body-supplied principals
- constant-time signature comparison
- continuation token stability
- file upload fetch policy
- failure delivery
Official Channel Integrations
| Channel | Official docs | Enterprise point |
|---|---|---|
| Eve HTTP | eve | session and stream routes |
| Custom | Custom Channels | HTTP/WS, metadata, cross-channel hand-off |
| Slack | Slack | thread anchoring and private auth challenges |
| Discord | Discord | slash commands, components, modals |
| GitHub | GitHub | PR diff context and sandbox checkout |
| Linear | Linear | Agent Sessions and Agent Activities |
| Teams | Microsoft Teams | Bot Framework and Adaptive Cards |
| Telegram | Telegram | inline-keyboard HITL |
| Twilio | Twilio | SMS/voice compliance review |
Client And Frontend
Use TypeScript SDK for scripts, tests, and server-to-server calls. Use frontend helpers for browser chat or agent UI.
| Topic | Standard |
|---|---|
| resumable sessions | persist the full cursor: sessionId, continuationToken, streamIndex |
clientContext | per-turn ephemeral context, not durable session history |
| HITL response | answer input.requested with inputResponses on the same session |
| framework integration | Next.js withEve, Nuxt module, SvelteKit plugin |
Checklist
| Item | Standard |
|---|---|
| route auth | no placeholder in production |
| session ACL | principal and tenant mapping |
| stream ACL | stream endpoint protected |
| continuation | latest cursor persisted; stale rejected |
| ordering | per-session queue if needed |
| metadata | project only safe values |
| signatures | constant-time verification |
| eval | 401, 403, and valid stream cases |
Ch8. Sandbox Secure Runtime
Turn Eve sandbox trust boundaries, backends, network policy, credential brokering, and workspace lifecycle into operating standards.
Ch10. Subagents, Workflows, Remote Agents
Design Eve 0.82.0 task-based delegation, root copies, isolated specialists, structured output, workflows, and remote authentication.