rivetkit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Rivet Actors

Rivet Actors

Build high-performance, long-lived, in-memory processes on Rivet's actor runtime. Use this skill whenever HTTP endpoints, databases, or queues no longer meet the requirements for AI agents, realtime collaboration, workflow orchestration, or geo-distributed data.
在Rivet的Actor运行时上构建高性能、长驻内存的进程。当HTTP端点、数据库或队列无法满足AI智能体、实时协作、工作流编排或地理分布式数据的需求时,即可使用本技能。

What To Do If You Get Stuck

遇到问题时的解决办法

If something is not working as intended or you are stuck, prompt the user to join the Rivet Discord or file an issue on the Rivet GitHub to report an issue and get help.
Provide the user with a pre-generated report with:
  • Symptoms
    • If this is happening in local dev, deployed, or both
    • The error you're seeing
    • Relevant source code related to this
  • What you've tried to solve it
  • Environment
    • RivetKit version
    • Runtime (Node, Bun, etc) including version
    • If applicable, provider in use (e.g. Vercel, Railway, etc)
    • If applicable, HTTP router in use (e.g. Hono, Express, Elysia)
如果功能未按预期运行或遇到瓶颈,提示用户加入Rivet Discord或在Rivet GitHub仓库提交问题以获取帮助。
为用户提供预先生成的报告,包含以下内容:
  • 症状
    • 问题发生在本地开发环境、已部署环境还是两者皆有
    • 遇到的错误信息
    • 相关的源代码
  • 已尝试的解决方法
  • 环境信息
    • RivetKit版本
    • 运行时(Node、Bun等)及对应版本
    • 若适用,使用的服务商(如Vercel、Railway等)
    • 若适用,使用的HTTP路由框架(如Hono、Express、Elysia)

Citing Sources

引用来源

When providing information from Rivet documentation, cite the canonical URL so users can learn more. Each reference file includes its canonical URL in the header metadata.
How to cite:
  • Use inline links for key concepts: "Use actor keys to uniquely identify instances."
  • Add a "Learn more" link after explanations for complex topics
Finding canonical URLs:
The Reference Map below links to reference files. Each file's header contains:
> Canonical URL: https://rivet.dev/docs/actors/actions
Use that canonical URL when citing, not the reference file path.
Examples:
  • Actions →
    https://rivet.dev/docs/actors/actions
  • React client →
    https://rivet.dev/docs/clients/react
  • Self-hosting on Kubernetes →
    https://rivet.dev/docs/self-hosting/kubernetes
当提供来自Rivet文档的信息时,请引用规范URL以便用户深入学习。每个参考文件的头部元数据中都包含其规范URL。
引用方式:
  • 关键概念使用内联链接:"使用actor keys唯一标识实例。"
  • 复杂主题的解释后添加“了解更多”链接
查找规范URL:
下方的参考映射表链接到各个参考文件。每个文件的头部包含:
> Canonical URL: https://rivet.dev/docs/actors/actions
引用时请使用该规范URL,而非参考文件路径。
示例:
  • Actions →
    https://rivet.dev/docs/actors/actions
  • React客户端 →
    https://rivet.dev/docs/clients/react
  • Kubernetes自托管 →
    https://rivet.dev/docs/self-hosting/kubernetes

First Steps

入门步骤

  1. Install RivetKit (latest: 2.0.42)
    bash
    npm install rivetkit@2.0.42
  2. Define a registry with
    setup({ use: { /* actors */ } })
    .
  3. Expose
    registry.serve()
    or
    registry.handler()
    (serverless) or
    registry.startRunner()
    (runner mode). Prefer serverless mode unless the user has a specific reason to use runner mode.
  4. Verify
    /api/rivet/metadata
    returns 200 before deploying.
  5. Configure Rivet Cloud or self-hosted engine
  6. Integrate clients (see client guides below for JavaScript, React, or Swift)
  7. Prompt the user if they want to deploy. If so, go to Deploying Rivet Backends.
For more information, read the quickstart guide relevant to the user's project.
  1. 安装RivetKit(最新版本:2.0.42)
    bash
    npm install rivetkit@2.0.42
  2. 使用
    setup({ use: { /* actors */ } })
    定义注册表。
  3. 暴露
    registry.serve()
    registry.handler()
    (无服务器模式)或
    registry.startRunner()
    (运行器模式)。除非用户有特定理由使用运行器模式,否则优先选择无服务器模式。
  4. 部署前验证
    /api/rivet/metadata
    返回200状态码。
  5. 配置Rivet Cloud或自托管引擎
  6. 集成客户端(请参阅下方的JavaScript、React或Swift客户端指南)
  7. 询问用户是否要进行部署。若是,进入“部署Rivet后端”环节。
如需更多信息,请阅读与用户项目相关的快速入门指南。

Deploying Rivet Backends

部署Rivet后端

Assume the user is deploying to Rivet Cloud, unless otherwise specified. If user is self-hosting, read the self-hosting guides below.
  1. Verify that Rivet Actors are working in local dev
  2. Prompt the user to choose a provider to deploy to (see Connect for a list of providers, such as Vercel, Railway, etc)
  3. Follow the deploy guide for that given provider. You will need to instruct the user when you need manual intervention.
除非另有说明,否则默认用户部署到Rivet Cloud。如果用户选择自托管,请参阅下方的自托管指南。
  1. 验证Rivet Actors在本地开发环境中正常工作
  2. 提示用户选择部署服务商(请参阅连接部分的服务商列表,如Vercel、Railway等)
  3. 遵循对应服务商的部署指南。当需要用户手动操作时,请给出明确指示。

Features

功能特性

  • Long-Lived, Stateful Compute: Each unit of compute is like a tiny server that remembers things between requests – no need to re-fetch data from a database or worry about timeouts. Like AWS Lambda, but with memory and no timeouts.
  • Blazing-Fast Reads & Writes: State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes. State is persisted to Rivet for long term storage, so it survives server restarts.
  • Realtime: Update state and broadcast changes in realtime with WebSockets. No external pub/sub systems, no polling – just built-in low-latency events.
  • Infinitely Scalable: Automatically scale from zero to millions of concurrent actors. Pay only for what you use with instant scaling and no cold starts.
  • Fault Tolerant: Built-in error handling and recovery. Actors automatically restart on failure while preserving state integrity and continuing operations.
  • 长驻内存的有状态计算:每个计算单元就像一个微型服务器,可在请求之间保留状态——无需从数据库重新获取数据,也无需担心超时问题。类似AWS Lambda,但具备内存持久化且无超时限制。
  • 极速读写:状态与计算存储在同一台机器上,因此读写速度极快。无需数据库往返,无延迟峰值。状态会持久化到Rivet以实现长期存储,因此即使服务器重启也不会丢失。
  • 实时性:通过WebSocket更新状态并广播变更。无需外部发布/订阅系统,无需轮询——内置低延迟事件机制。
  • 无限扩展:自动从0扩展到数百万个并发Actor。按实际使用量付费,即时扩展且无冷启动问题。
  • 容错性:内置错误处理与恢复机制。Actor在故障时会自动重启,同时保持状态完整性并继续运行。

When to Use Rivet Actors

Rivet Actors的适用场景

  • AI agents & sandboxes: multi-step toolchains, conversation memory, sandbox orchestration.
  • Multiplayer or collaborative apps: CRDT docs, shared cursors, realtime dashboards, chat.
  • Workflow automation: background jobs, cron, rate limiters, durable queues, backpressure control.
  • Data-intensive backends: geo-distributed or per-tenant databases, in-memory caches, sharded SQL.
  • Networking workloads: WebSocket servers, custom protocols, local-first sync, edge fanout.
  • AI智能体与沙箱:多步骤工具链、对话记忆、沙箱编排。
  • 多人协作应用:CRDT文档、共享光标、实时仪表板、聊天应用。
  • 工作流自动化:后台任务、定时任务、速率限制器、持久化队列、背压控制。
  • 数据密集型后端:地理分布式或按租户划分的数据库、内存缓存、分片SQL。
  • 网络工作负载:WebSocket服务器、自定义协议、本地优先同步、边缘扇出。

Common Patterns

常见模式

Actors scale naturally through isolated state and message-passing. Structure your applications with these patterns:
Actor通过隔离状态与消息传递实现自然扩展。请使用以下模式构建应用:

Actor Per Entity

每个实体对应一个Actor

Create one actor per user, document, or room. Use compound keys to scope entities:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// Single key: one actor per user
client.user.getOrCreate(["user-123"]);

// Compound key: document scoped to an organization
client.document.getOrCreate(["org-acme", "doc-456"]);
ts
import { actor, setup } from "rivetkit";

export const user = actor({
  state: { name: "" },
  actions: {},
});

export const document = actor({
  state: { content: "" },
  actions: {},
});

export const registry = setup({ use: { user, document } });
为每个用户、文档或房间创建一个Actor。使用复合键对实体进行范围限定:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// 单键:每个用户对应一个Actor
client.user.getOrCreate(["user-123"]);

// 复合键:组织范围内的文档
client.document.getOrCreate(["org-acme", "doc-456"]);
ts
import { actor, setup } from "rivetkit";

export const user = actor({
  state: { name: "" },
  actions: {},
});

export const document = actor({
  state: { content: "" },
  actions: {},
});

export const registry = setup({ use: { user, document } });

Coordinator & Data Actors

协调者与数据Actor

Data actors handle core logic (chat rooms, game sessions, user data). Coordinator actors track and manage collections of data actors—think of them as an index.
ts
import { actor, setup } from "rivetkit";

// Coordinator: tracks chat rooms within an organization
export const chatRoomList = actor({
  state: { rooms: [] as string[] },
  actions: {
    addRoom: async (c, name: string) => {
      // Create the chat room actor
      const client = c.client<typeof registry>();
      await client.chatRoom.create([c.key[0], name]);
      c.state.rooms.push(name);
    },
    listRooms: (c) => c.state.rooms,
  },
});

// Data actor: handles a single chat room
export const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    send: (c, msg: string) => { c.state.messages.push(msg); },
  },
});

export const registry = setup({ use: { chatRoomList, chatRoom } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// Coordinator per org
const coordinator = client.chatRoomList.getOrCreate(["org-acme"]);
await coordinator.addRoom("general");
await coordinator.addRoom("random");

// Access chat rooms created by coordinator
client.chatRoom.get(["org-acme", "general"]);
数据Actor处理核心逻辑(聊天室、游戏会话、用户数据)。协调者Actor跟踪并管理数据Actor的集合——可将其视为索引。
ts
import { actor, setup } from "rivetkit";

// 协调者:跟踪组织内的聊天室
export const chatRoomList = actor({
  state: { rooms: [] as string[] },
  actions: {
    addRoom: async (c, name: string) => {
      // 创建聊天室Actor
      const client = c.client<typeof registry>();
      await client.chatRoom.create([c.key[0], name]);
      c.state.rooms.push(name);
    },
    listRooms: (c) => c.state.rooms,
  },
});

// 数据Actor:处理单个聊天室
export const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    send: (c, msg: string) => { c.state.messages.push(msg); },
  },
});

export const registry = setup({ use: { chatRoomList, chatRoom } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// 每个组织对应一个协调者
const coordinator = client.chatRoomList.getOrCreate(["org-acme"]);
await coordinator.addRoom("general");
await coordinator.addRoom("random");

// 访问由协调者创建的聊天室
client.chatRoom.get(["org-acme", "general"]);

Sharding

分片

Split high-load actors by time, user ID, or random key:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// Shard by hour
const hour = new Date().toISOString().slice(0, 13); // "2024-01-15T09"
client.analytics.getOrCreate(["org-acme", hour]);

// Shard randomly across 3 actors
client.rateLimiter.getOrCreate([`shard-${Math.floor(Math.random() * 3)}`]);
ts
import { actor, setup } from "rivetkit";

export const analytics = actor({
  state: { events: [] as string[] },
  actions: {},
});

export const rateLimiter = actor({
  state: { requests: 0 },
  actions: {},
});

export const registry = setup({ use: { analytics, rateLimiter } });
按时间、用户ID或随机键拆分高负载Actor:
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./actors";

const client = createClient<typeof registry>();

// 按小时分片
const hour = new Date().toISOString().slice(0, 13); // "2024-01-15T09"
client.analytics.getOrCreate(["org-acme", hour]);

// 随机分片到3个Actor
client.rateLimiter.getOrCreate([`shard-${Math.floor(Math.random() * 3)}`]);
ts
import { actor, setup } from "rivetkit";

export const analytics = actor({
  state: { events: [] as string[] },
  actions: {},
});

export const rateLimiter = actor({
  state: { requests: 0 },
  actions: {},
});

export const registry = setup({ use: { analytics, rateLimiter } });

Fan-In & Fan-Out

扇出与扇入

Distribute work across workers (fan-out) and aggregate results (fan-in):
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface Task { id: string; data: string; }
interface Result { taskId: string; output: string; }

const coordinator = actor({
  state: { results: [] as Result[] },
  actions: {
    // Fan-out: distribute work in parallel
    startJob: async (c, tasks: Task[]) => {
      const client = c.client<typeof registry>();
      await Promise.all(
        tasks.map(t => client.worker.getOrCreate([t.id]).process(t))
      );
    },
    // Fan-in: collect results
    reportResult: (c, result: Result) => { c.state.results.push(result); },
  },
});

const worker = actor({
  state: {},
  actions: {
    process: async (c, task: Task) => {
      const result = { taskId: task.id, output: `Processed ${task.data}` };
      const client = c.client<typeof registry>();
      await client.coordinator.getOrCreate(["org-acme"]).reportResult(result);
    },
  },
});

const registry = setup({ use: { coordinator, worker } });
将工作分配到多个Worker(扇出)并聚合结果(扇入):
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface Task { id: string; data: string; }
interface Result { taskId: string; output: string; }

const coordinator = actor({
  state: { results: [] as Result[] },
  actions: {
    // 扇出:并行分配工作
    startJob: async (c, tasks: Task[]) => {
      const client = c.client<typeof registry>();
      await Promise.all(
        tasks.map(t => client.worker.getOrCreate([t.id]).process(t))
      );
    },
    // 扇入:收集结果
    reportResult: (c, result: Result) => { c.state.results.push(result); },
  },
});

const worker = actor({
  state: {},
  actions: {
    process: async (c, task: Task) => {
      const result = { taskId: task.id, output: `Processed ${task.data}` };
      const client = c.client<typeof registry>();
      await client.coordinator.getOrCreate(["org-acme"]).reportResult(result);
    },
  },
});

const registry = setup({ use: { coordinator, worker } });

Anti-Patterns

反模式

"God" actor

“上帝”Actor

Avoid a single actor that handles everything. This creates a bottleneck and defeats the purpose of the actor model. Split into focused actors per entity instead.
避免使用单个Actor处理所有事务。这会造成瓶颈,违背Actor模型的初衷。应将其拆分为每个实体对应一个专注的Actor。

Actor-per-request

每个请求对应一个Actor

Actors maintain state across requests. Creating one per request wastes resources and loses the benefits of persistent state. Use actors for persistent entities and regular functions for stateless work.
Actor会在请求之间保留状态。为每个请求创建一个Actor会浪费资源,并失去持久化状态的优势。请为持久化实体使用Actor,无状态工作使用常规函数。

Minimal Project

最小化项目示例

Backend

后端

actors.ts
ts
import { actor, setup } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c, amount: number) => {
      c.state.count += amount;
      c.broadcast("count", c.state.count);
      return c.state.count;
    },
  },
});

export const registry = setup({
  use: { counter },
});
server.ts
Integrate with the user's existing server if applicable. Otherwise, default to Hono.
actors.ts
ts
import { actor, setup } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c, amount: number) => {
      c.state.count += amount;
      c.broadcast("count", c.state.count);
      return c.state.count;
    },
  },
});

export const registry = setup({
  use: { counter },
});
server.ts
如果适用,集成到用户现有的服务器中。否则默认使用Hono。

No Framework

无框架

typescript
import { actor, setup } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// Exposes Rivet API on /api/rivet/ to communicate with actors
export default registry.serve();
typescript
import { actor, setup } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// 在/api/rivet/路径暴露Rivet API以与Actor通信
export default registry.serve();

Hono

Hono

typescript
import { Hono } from "hono";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// Build client to communicate with actors (optional)
const client = createClient<typeof registry>();

const app = new Hono();

// Exposes Rivet API to communicate with actors
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));

export default app;
typescript
import { Hono } from "hono";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// 构建客户端以与Actor通信(可选)
const client = createClient<typeof registry>();

const app = new Hono();

// 暴露Rivet API以与Actor通信
app.all("/api/rivet/*", (c) => registry.handler(c.req.raw));

export default app;

Elysia

Elysia

typescript
import { Elysia } from "elysia";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// Build client to communicate with actors (optional)
const client = createClient<typeof registry>();

const app = new Elysia()
	// Exposes Rivet API to communicate with actors
	.all("/api/rivet/*", (c) => registry.handler(c.request));

export default app;
typescript
import { Elysia } from "elysia";
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c, amount: number) => c.state.count += amount }
});

const registry = setup({ use: { counter } });

// 构建客户端以与Actor通信(可选)
const client = createClient<typeof registry>();

const app = new Elysia()
	// 暴露Rivet API以与Actor通信
	.all("/api/rivet/*", (c) => registry.handler(c.request));

export default app;

Client Docs

客户端文档

Use the client SDK that matches your app:
使用与应用匹配的客户端SDK:

Actor Quick Reference

Actor快速参考

State

状态

Persistent data that survives restarts, crashes, and deployments. State is persisted on Rivet Cloud or Rivet self-hosted, so it survives restarts if the current process crashes or exits.
持久化数据可在重启、崩溃和部署后保留。状态会持久化到Rivet Cloud或自托管Rivet中,因此即使当前进程崩溃或退出,状态也不会丢失。

Static Initial State

静态初始状态

ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c) => c.state.count += 1,
  },
});
ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c) => c.state.count += 1,
  },
});

Dynamic Initial State

动态初始状态

ts
import { actor } from "rivetkit";

interface CounterState {
  count: number;
}

const counter = actor({
  state: { count: 0 } as CounterState,
  createState: (c, input: { start?: number }): CounterState => ({
    count: input.start ?? 0,
  }),
  actions: {
    increment: (c) => c.state.count += 1,
  },
});
ts
import { actor } from "rivetkit";

interface CounterState {
  count: number;
}

const counter = actor({
  state: { count: 0 } as CounterState,
  createState: (c, input: { start?: number }): CounterState => ({
    count: input.start ?? 0,
  }),
  actions: {
    increment: (c) => c.state.count += 1,
  },
});

Keys

Keys uniquely identify actor instances. Use compound keys (arrays) for hierarchical addressing:
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    getRoomInfo: (c) => ({ org: c.key[0], room: c.key[1] }),
  },
});

const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();

// Compound key: [org, room]
client.chatRoom.getOrCreate(["org-acme", "general"]);

// Access key inside actor via c.key
Don't build keys with string interpolation like
"org:${userId}"
when
userId
contains user data. Use arrays instead to prevent key injection attacks.
键用于唯一标识Actor实例。使用复合键(数组)进行分层寻址:
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    getRoomInfo: (c) => ({ org: c.key[0], room: c.key[1] }),
  },
});

const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>();

// 复合键:[组织, 房间]
client.chatRoom.getOrCreate(["org-acme", "general"]);

// 在Actor内部通过c.key访问键
userId
包含用户数据时,请勿使用字符串插值(如
"org:${userId}"
)构建键。请使用数组以防止键注入攻击。

Input

输入

Pass initialization data when creating actors.
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const game = actor({
  createState: (c, input: { mode: string }) => ({ mode: input.mode }),
  actions: {},
});

const registry = setup({ use: { game } });
const client = createClient<typeof registry>();

// Client usage
const gameHandle = client.game.getOrCreate(["game-1"], {
  createWithInput: { mode: "ranked" }
});
创建Actor时传递初始化数据。
ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const game = actor({
  createState: (c, input: { mode: string }) => ({ mode: input.mode }),
  actions: {},
});

const registry = setup({ use: { game } });
const client = createClient<typeof registry>();

// 客户端使用
const gameHandle = client.game.getOrCreate(["game-1"], {
  createWithInput: { mode: "ranked" }
});

Temporary Variables

临时变量

Temporary data that doesn't survive restarts. Use for non-serializable objects (event emitters, connections, etc).
临时数据无法在重启后保留。适用于非序列化对象(如事件发射器、连接等)。

Static Initial Vars

静态初始变量

ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  vars: { lastAccess: 0 },
  actions: {
    increment: (c) => {
      c.vars.lastAccess = Date.now();
      return c.state.count += 1;
    },
  },
});
ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  vars: { lastAccess: 0 },
  actions: {
    increment: (c) => {
      c.vars.lastAccess = Date.now();
      return c.state.count += 1;
    },
  },
});

Dynamic Initial Vars

动态初始变量

ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  createVars: () => ({
    emitter: new EventTarget(),
  }),
  actions: {
    increment: (c) => {
      c.vars.emitter.dispatchEvent(new Event("change"));
      return c.state.count += 1;
    },
  },
});
ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  createVars: () => ({
    emitter: new EventTarget(),
  }),
  actions: {
    increment: (c) => {
      c.vars.emitter.dispatchEvent(new Event("change"));
      return c.state.count += 1;
    },
  },
});

Actions

动作

Actions are the primary way clients and other actors communicate with an actor.
ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c, amount: number) => (c.state.count += amount),
    getCount: (c) => c.state.count,
  },
});
动作是客户端与其他Actor和目标Actor通信的主要方式。
ts
import { actor } from "rivetkit";

const counter = actor({
  state: { count: 0 },
  actions: {
    increment: (c, amount: number) => (c.state.count += amount),
    getCount: (c) => c.state.count,
  },
});

Events & Broadcasts

事件与广播

Events enable real-time communication from actors to connected clients.
ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    sendMessage: (c, text: string) => {
      // Broadcast to ALL connected clients
      c.broadcast("newMessage", { text });
    },
  },
});
事件支持Actor向已连接客户端进行实时通信。
ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    sendMessage: (c, text: string) => {
      // 广播到所有已连接客户端
      c.broadcast("newMessage", { text });
    },
  },
});

Connections

连接

Access the current connection via
c.conn
or all connected clients via
c.conns
. Use
c.conn.id
or
c.conn.state
to securely identify who is calling an action. Connection state is initialized via
connState
or
createConnState
, which receives parameters passed by the client on connect.
通过
c.conn
访问当前连接,或通过
c.conns
访问所有已连接客户端。使用
c.conn.id
c.conn.state
安全识别调用动作的用户——绝不要信任作为动作参数传递的用户ID。连接状态通过
connState
createConnState
初始化,接收客户端连接时传递的参数。

Static Connection Initial State

静态连接初始状态

ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: {},
  connState: { visitorId: 0 },
  onConnect: (c, conn) => {
    conn.state.visitorId = Math.random();
  },
  actions: {
    whoAmI: (c) => c.conn.state.visitorId,
  },
});
ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: {},
  connState: { visitorId: 0 },
  onConnect: (c, conn) => {
    conn.state.visitorId = Math.random();
  },
  actions: {
    whoAmI: (c) => c.conn.state.visitorId,
  },
});

Dynamic Connection Initial State

动态连接初始状态

ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: {},
  // params passed from client
  createConnState: (c, params: { userId: string }) => ({
    userId: params.userId,
  }),
  actions: {
    // Access current connection's state and params
    whoAmI: (c) => ({
      state: c.conn.state,
      params: c.conn.params,
    }),
    // Iterate all connections with c.conns
    notifyOthers: (c, text: string) => {
      for (const conn of c.conns.values()) {
        if (conn !== c.conn) conn.send("notification", { text });
      }
    },
  },
});
ts
import { actor } from "rivetkit";

const chatRoom = actor({
  state: {},
  // 客户端传递的参数
  createConnState: (c, params: { userId: string }) => ({
    userId: params.userId,
  }),
  actions: {
    // 访问当前连接的状态和参数
    whoAmI: (c) => ({
      state: c.conn.state,
      params: c.conn.params,
    }),
    // 通过c.conns遍历所有连接
    notifyOthers: (c, text: string) => {
      for (const conn of c.conns.values()) {
        if (conn !== c.conn) conn.send("notification", { text });
      }
    },
  },
});

Actor-to-Actor Communication

Actor间通信

Actors can call other actors using
c.client()
.
ts
import { actor, setup } from "rivetkit";

const inventory = actor({
  state: { stock: 100 },
  actions: {
    reserve: (c, amount: number) => { c.state.stock -= amount; }
  }
});

const order = actor({
  state: {},
  actions: {
    process: async (c) => {
      const client = c.client<typeof registry>();
      await client.inventory.getOrCreate(["main"]).reserve(1);
    },
  },
});

const registry = setup({ use: { inventory, order } });
Actor可使用
c.client()
调用其他Actor。
ts
import { actor, setup } from "rivetkit";

const inventory = actor({
  state: { stock: 100 },
  actions: {
    reserve: (c, amount: number) => { c.state.stock -= amount; }
  }
});

const order = actor({
  state: {},
  actions: {
    process: async (c) => {
      const client = c.client<typeof registry>();
      await client.inventory.getOrCreate(["main"]).reserve(1);
    },
  },
});

const registry = setup({ use: { inventory, order } });

Scheduling

调度

Schedule actions to run after a delay or at a specific time. Schedules persist across restarts, upgrades, and crashes.
ts
import { actor } from "rivetkit";

const reminder = actor({
  state: { message: "" },
  actions: {
    // Schedule action to run after delay (ms)
    setReminder: (c, message: string, delayMs: number) => {
      c.state.message = message;
      c.schedule.after(delayMs, "sendReminder");
    },
    // Schedule action to run at specific timestamp
    setReminderAt: (c, message: string, timestamp: number) => {
      c.state.message = message;
      c.schedule.at(timestamp, "sendReminder");
    },
    sendReminder: (c) => {
      c.broadcast("reminder", { message: c.state.message });
    },
  },
});
调度动作在延迟一段时间后或特定时间运行。调度会在重启、升级和崩溃后保留。
ts
import { actor } from "rivetkit";

const reminder = actor({
  state: { message: "" },
  actions: {
    // 调度动作在延迟后运行(毫秒)
    setReminder: (c, message: string, delayMs: number) => {
      c.state.message = message;
      c.schedule.after(delayMs, "sendReminder");
    },
    // 调度动作在特定时间戳运行
    setReminderAt: (c, message: string, timestamp: number) => {
      c.state.message = message;
      c.schedule.at(timestamp, "sendReminder");
    },
    sendReminder: (c) => {
      c.broadcast("reminder", { message: c.state.message });
    },
  },
});

Destroying Actors

销毁Actor

Permanently delete an actor and its state using
c.destroy()
.
ts
import { actor } from "rivetkit";

const userAccount = actor({
  state: { email: "", name: "" },
  onDestroy: (c) => {
    console.log(`Account ${c.state.email} deleted`);
  },
  actions: {
    deleteAccount: (c) => {
      c.destroy();
    },
  },
});
使用
c.destroy()
永久删除Actor及其状态。
ts
import { actor } from "rivetkit";

const userAccount = actor({
  state: { email: "", name: "" },
  onDestroy: (c) => {
    console.log(`Account ${c.state.email} deleted`);
  },
  actions: {
    deleteAccount: (c) => {
      c.destroy();
    },
  },
});

Lifecycle Hooks

生命周期钩子

Actors support hooks for initialization, connections, networking, and state changes.
ts
import { actor } from "rivetkit";

interface RoomState {
  users: Record<string, boolean>;
  name?: string;
}

interface RoomInput {
  roomName: string;
}

interface ConnState {
  userId: string;
  joinedAt: number;
}

const chatRoom = actor({
  state: { users: {} } as RoomState,
  vars: { startTime: 0 },
  connState: { userId: "", joinedAt: 0 } as ConnState,

  // State & vars initialization
  createState: (c, input: RoomInput): RoomState => ({ users: {}, name: input.roomName }),
  createVars: () => ({ startTime: Date.now() }),

  // Actor lifecycle
  onCreate: (c) => console.log("created", c.key),
  onDestroy: (c) => console.log("destroyed"),
  onWake: (c) => console.log("actor started"),
  onSleep: (c) => console.log("actor sleeping"),
  onStateChange: (c, newState) => c.broadcast("stateChanged", newState),

  // Connection lifecycle
  createConnState: (c, params): ConnState => ({ userId: (params as { userId: string }).userId, joinedAt: Date.now() }),
  onBeforeConnect: (c, params) => { /* validate auth */ },
  onConnect: (c, conn) => console.log("connected:", conn.state.userId),
  onDisconnect: (c, conn) => console.log("disconnected:", conn.state.userId),

  // Networking
  onRequest: (c, req) => new Response(JSON.stringify(c.state)),
  onWebSocket: (c, socket) => socket.addEventListener("message", console.log),

  // Response transformation
  onBeforeActionResponse: <Out>(c: unknown, name: string, args: unknown[], output: Out): Out => output,

  actions: {},
});
Actor支持初始化、连接、网络和状态变更的钩子。
ts
import { actor } from "rivetkit";

interface RoomState {
  users: Record<string, boolean>;
  name?: string;
}

interface RoomInput {
  roomName: string;
}

interface ConnState {
  userId: string;
  joinedAt: number;
}

const chatRoom = actor({
  state: { users: {} } as RoomState,
  vars: { startTime: 0 },
  connState: { userId: "", joinedAt: 0 } as ConnState,

  // 状态与变量初始化
  createState: (c, input: RoomInput): RoomState => ({ users: {}, name: input.roomName }),
  createVars: () => ({ startTime: Date.now() }),

  // Actor生命周期
  onCreate: (c) => console.log("created", c.key),
  onDestroy: (c) => console.log("destroyed"),
  onWake: (c) => console.log("actor started"),
  onSleep: (c) => console.log("actor sleeping"),
  onStateChange: (c, newState) => c.broadcast("stateChanged", newState),

  // 连接生命周期
  createConnState: (c, params): ConnState => ({ userId: (params as { userId: string }).userId, joinedAt: Date.now() }),
  onBeforeConnect: (c, params) => { /* 验证权限 */ },
  onConnect: (c, conn) => console.log("connected:", conn.state.userId),
  onDisconnect: (c, conn) => console.log("disconnected:", conn.state.userId),

  // 网络
  onRequest: (c, req) => new Response(JSON.stringify(c.state)),
  onWebSocket: (c, socket) => socket.addEventListener("message", console.log),

  // 响应转换
  onBeforeActionResponse: <Out>(c: unknown, name: string, args: unknown[], output: Out): Out => output,

  actions: {},
});

Helper Types

辅助类型

Use
ActionContextOf
to extract the context type for writing standalone helper functions:
ts
import { actor, ActionContextOf } from "rivetkit";

const gameRoom = actor({
  state: { players: [] as string[], score: 0 },
  actions: {
    addPlayer: (c, playerId: string) => {
      validatePlayer(c, playerId);
      c.state.players.push(playerId);
    },
  },
});

// Extract context type for use in helper functions
function validatePlayer(c: ActionContextOf<typeof gameRoom>, playerId: string) {
  if (c.state.players.includes(playerId)) {
    throw new Error("Player already in room");
  }
}
使用
ActionContextOf
提取上下文类型以编写独立的辅助函数:
ts
import { actor, ActionContextOf } from "rivetkit";

const gameRoom = actor({
  state: { players: [] as string[], score: 0 },
  actions: {
    addPlayer: (c, playerId: string) => {
      validatePlayer(c, playerId);
      c.state.players.push(playerId);
    },
  },
});

// 提取上下文类型用于辅助函数
function validatePlayer(c: ActionContextOf<typeof gameRoom>, playerId: string) {
  if (c.state.players.includes(playerId)) {
    throw new Error("Player already in room");
  }
}

Errors

错误处理

Use
UserError
to throw errors that are safely returned to clients. Pass
metadata
to include structured data. Other errors are converted to generic "internal error" for security.
使用
UserError
抛出可安全返回给客户端的错误。传递
metadata
以包含结构化数据。其他错误会转换为通用的“内部错误”以保障安全。

Actor

Actor端

ts
import { actor, UserError } from "rivetkit";

const user = actor({
  state: { username: "" },
  actions: {
    updateUsername: (c, username: string) => {
      if (username.length < 3) {
        throw new UserError("Username too short", {
          code: "username_too_short",
          metadata: { minLength: 3, actual: username.length },
        });
      }
      c.state.username = username;
    },
  },
});
ts
import { actor, UserError } from "rivetkit";

const user = actor({
  state: { username: "" },
  actions: {
    updateUsername: (c, username: string) => {
      if (username.length < 3) {
        throw new UserError("用户名过短", {
          code: "username_too_short",
          metadata: { minLength: 3, actual: username.length },
        });
      }
      c.state.username = username;
    },
  },
});

Client

客户端

ts
import { actor, setup } from "rivetkit";
import { createClient, ActorError } from "rivetkit/client";

const user = actor({
  state: { username: "" },
  actions: { updateUsername: (c, username: string) => { c.state.username = username; } }
});

const registry = setup({ use: { user } });
const client = createClient<typeof registry>();

try {
  await client.user.getOrCreate([]).updateUsername("ab");
} catch (error) {
  if (error instanceof ActorError) {
    console.log(error.code);     // "username_too_short"
    console.log(error.metadata); // { minLength: 3, actual: 2 }
  }
}
ts
import { actor, setup } from "rivetkit";
import { createClient, ActorError } from "rivetkit/client";

const user = actor({
  state: { username: "" },
  actions: { updateUsername: (c, username: string) => { c.state.username = username; } }
});

const registry = setup({ use: { user } });
const client = createClient<typeof registry>();

try {
  await client.user.getOrCreate([]).updateUsername("ab");
} catch (error) {
  if (error instanceof ActorError) {
    console.log(error.code);     // "username_too_short"
    console.log(error.metadata); // { minLength: 3, actual: 2 }
  }
}

Low-Level HTTP & WebSocket Handlers

底层HTTP与WebSocket处理器

For custom protocols or integrating libraries that need direct access to HTTP
Request
/
Response
or WebSocket connections, use
onRequest
and
onWebSocket
.
对于自定义协议或需要直接访问HTTP
Request
/
Response
或WebSocket连接的集成库,请使用
onRequest
onWebSocket

HTTP Handler

HTTP处理器

ts
import { actor, setup } from "rivetkit";

export const api = actor({
  state: { count: 0 },
  onRequest: (c, request) => {
    if (request.method === "POST") c.state.count++;
    return Response.json(c.state);
  },
  actions: {},
});

export const registry = setup({ use: { api } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";

const client = createClient<typeof registry>();
const actor = client.api.getOrCreate(["my-actor"]);

// Use built-in fetch method
const response = await actor.fetch("/count");

// Or get raw URL for external tools
const url = await actor.getGatewayUrl();
const nativeResponse = await fetch(`${url}/request/count`);
ts
import { actor, setup } from "rivetkit";

export const api = actor({
  state: { count: 0 },
  onRequest: (c, request) => {
    if (request.method === "POST") c.state.count++;
    return Response.json(c.state);
  },
  actions: {},
});

export const registry = setup({ use: { api } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";

const client = createClient<typeof registry>();
const actor = client.api.getOrCreate(["my-actor"]);

// 使用内置的fetch方法
const response = await actor.fetch("/count");

// 或获取原始URL供外部工具使用
const url = await actor.getGatewayUrl();
const nativeResponse = await fetch(`${url}/request/count`);

WebSocket Handler

WebSocket处理器

ts
import { actor, setup } from "rivetkit";

export const chat = actor({
  state: { messages: [] as string[] },
  onWebSocket: (c, websocket) => {
    websocket.addEventListener("open", () => {
      websocket.send(JSON.stringify({ type: "history", messages: c.state.messages }));
    });
    websocket.addEventListener("message", (event) => {
      c.state.messages.push(event.data as string);
      websocket.send(event.data as string);
      c.saveState({ immediate: true });
    });
  },
  actions: {},
});

export const registry = setup({ use: { chat } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";

const client = createClient<typeof registry>();
const actor = client.chat.getOrCreate(["my-chat"]);

// Use built-in websocket method
const ws = await actor.websocket("/");

// Or get raw URL for external tools
const url = await actor.getGatewayUrl();
const nativeWs = new WebSocket(`${url.replace("http://", "ws://").replace("https://", "wss://")}/websocket/`);
ts
import { actor, setup } from "rivetkit";

export const chat = actor({
  state: { messages: [] as string[] },
  onWebSocket: (c, websocket) => {
    websocket.addEventListener("open", () => {
      websocket.send(JSON.stringify({ type: "history", messages: c.state.messages }));
    });
    websocket.addEventListener("message", (event) => {
      c.state.messages.push(event.data as string);
      websocket.send(event.data as string);
      c.saveState({ immediate: true });
    });
  },
  actions: {},
});

export const registry = setup({ use: { chat } });
ts
import { createClient } from "rivetkit/client";
import type { registry } from "./registry";

const client = createClient<typeof registry>();
const actor = client.chat.getOrCreate(["my-chat"]);

// 使用内置的websocket方法
const ws = await actor.websocket("/");

// 或获取原始URL供外部工具使用
const url = await actor.getGatewayUrl();
const nativeWs = new WebSocket(`${url.replace("http://", "ws://").replace("https://", "wss://")}/websocket/`);

Versions & Upgrades

版本与升级

When deploying new code, configure version numbers to control how actors are upgraded:
ts
import { actor, setup } from "rivetkit";

const myActor = actor({ state: {}, actions: {} });

const registry = setup({
  use: { myActor },
  runner: {
    version: 2, // Increment on each deployment
  },
});
Or use environment variable:
RIVET_RUNNER_VERSION=2
Common version sources:
  • Build timestamp:
    Date.now()
  • Git commit count:
    git rev-list --count HEAD
  • CI build number:
    github.run_number
    ,
    GITHUB_RUN_NUMBER
    , etc.
部署新代码时,请配置版本号以控制Actor的升级方式:
ts
import { actor, setup } from "rivetkit";

const myActor = actor({ state: {}, actions: {} });

const registry = setup({
  use: { myActor },
  runner: {
    version: 2, // 每次部署时递增
  },
});
或使用环境变量:
RIVET_RUNNER_VERSION=2
常见版本来源:
  • 构建时间戳
    Date.now()
  • Git提交次数
    git rev-list --count HEAD
  • CI构建编号
    github.run_number
    ,
    GITHUB_RUN_NUMBER

Client Documentation

客户端文档

Find the full client guides here:
完整的客户端指南请参阅:

Authentication & Security

认证与安全

Validate credentials in
onBeforeConnect
or
createConnState
. Throw an error to reject the connection. Use
c.conn.id
or
c.conn.state
to identify users in actions—never trust user IDs passed as action parameters.
ts
import { actor, UserError } from "rivetkit";

// Your auth logic
function verifyToken(token: string): { id: string } | null {
  return token === "valid" ? { id: "user123" } : null;
}

const chatRoom = actor({
  state: { messages: [] as string[] },

  createConnState: (_c, params: { token: string }) => {
    const user = verifyToken(params.token);
    if (!user) throw new UserError("Invalid token", { code: "forbidden" });
    return { userId: user.id };
  },

  actions: {
    send: (c, text: string) => {
      // Use c.conn.state for secure identity, not action parameters
      const connState = c.conn.state as { userId: string };
      c.state.messages.push(`${connState.userId}: ${text}`);
    },
  },
});
onBeforeConnect
createConnState
中验证凭据。抛出错误以拒绝连接。在动作中使用
c.conn.id
c.conn.state
识别用户——绝不要信任作为动作参数传递的用户ID。
ts
import { actor, UserError } from "rivetkit";

// 你的认证逻辑
function verifyToken(token: string): { id: string } | null {
  return token === "valid" ? { id: "user123" } : null;
}

const chatRoom = actor({
  state: { messages: [] as string[] },

  createConnState: (_c, params: { token: string }) => {
    const user = verifyToken(params.token);
    if (!user) throw new UserError("无效令牌", { code: "forbidden" });
    return { userId: user.id };
  },

  actions: {
    send: (c, text: string) => {
      // 使用c.conn.state进行安全身份识别,而非动作参数
      const connState = c.conn.state as { userId: string };
      c.state.messages.push(`${connState.userId}: ${text}`);
    },
  },
});

CORS (Cross-Origin Resource Sharing)

CORS(跨域资源共享)

Validate origins in
onBeforeConnect
to control which domains can access your actors:
ts
import { actor, UserError } from "rivetkit";

const myActor = actor({
  state: { count: 0 },
  onBeforeConnect: (c) => {
    const origin = c.request?.headers.get("origin");
    if (origin !== "https://myapp.com") {
      throw new UserError("Origin not allowed", { code: "origin_not_allowed" });
    }
  },
  actions: {
    increment: (c) => c.state.count++,
  },
});
onBeforeConnect
中验证来源以控制哪些域名可以访问你的Actor:
ts
import { actor, UserError } from "rivetkit";

const myActor = actor({
  state: { count: 0 },
  onBeforeConnect: (c) => {
    const origin = c.request?.headers.get("origin");
    if (origin !== "https://myapp.com") {
      throw new UserError("来源不被允许", { code: "origin_not_allowed" });
    }
  },
  actions: {
    increment: (c) => c.state.count++,
  },
});

API Reference

API参考

The RivetKit OpenAPI specification is available in the skill directory at
openapi.json
. This file documents all HTTP endpoints for managing actors.
RivetKit的OpenAPI规范可在技能目录的
openapi.json
文件中找到。该文件记录了管理Actor的所有HTTP端点。

Reference Map

参考映射表

Actors

Actors

  • Actions
  • Actor Keys
  • Actor Scheduling
  • AI and User-Generated Rivet Actors
  • Authentication
  • Cloudflare Workers Quickstart
  • Communicating Between Actors
  • Connections
  • Design Patterns
  • Destroying Actors
  • Ephemeral Variables
  • Errors
  • Events
  • External SQL Database
  • Fetch and WebSocket Handler
  • Helper Types
  • Input Parameters
  • Lifecycle
  • Low-Level HTTP Request Handler
  • Low-Level KV Storage
  • Low-Level WebSocket Handler
  • Metadata
  • Next.js Quickstart
  • Node.js & Bun Quickstart
  • React Quickstart
  • Scaling & Concurrency
  • Sharing and Joining State
  • State
  • Testing
  • Types
  • Vanilla HTTP API
  • Versions & Upgrades
  • Actions
  • Actor Keys
  • Actor Scheduling
  • AI and User-Generated Rivet Actors
  • Authentication
  • Cloudflare Workers Quickstart
  • Communicating Between Actors
  • Connections
  • Design Patterns
  • Destroying Actors
  • Ephemeral Variables
  • Errors
  • Events
  • External SQL Database
  • Fetch and WebSocket Handler
  • Helper Types
  • Input Parameters
  • Lifecycle
  • Low-Level HTTP Request Handler
  • Low-Level KV Storage
  • Low-Level WebSocket Handler
  • Metadata
  • Next.js Quickstart
  • Node.js & Bun Quickstart
  • React Quickstart
  • Scaling & Concurrency
  • Sharing and Joining State
  • State
  • Testing
  • Types
  • Vanilla HTTP API
  • Versions & Upgrades

Clients

Clients

  • Node.js & Bun
  • React
  • Swift
  • SwiftUI
  • Node.js & Bun
  • React
  • Swift
  • SwiftUI

Connect

Connect

  • Deploy To Amazon Web Services Lambda
  • Deploying to AWS ECS
  • Deploying to Cloudflare Workers
  • Deploying to Freestyle
  • Deploying to Google Cloud Run
  • Deploying to Hetzner
  • Deploying to Kubernetes
  • Deploying to Railway
  • Deploying to Vercel
  • Deploying to VMs & Bare Metal
  • Supabase
  • Deploy To Amazon Web Services Lambda
  • Deploying to AWS ECS
  • Deploying to Cloudflare Workers
  • Deploying to Freestyle
  • Deploying to Google Cloud Run
  • Deploying to Hetzner
  • Deploying to Kubernetes
  • Deploying to Railway
  • Deploying to Vercel
  • Deploying to VMs & Bare Metal
  • Supabase

General

General

  • Actor Configuration
  • Architecture
  • Cross-Origin Resource Sharing
  • Documentation for LLMs & AI
  • Edge Networking
  • Endpoints
  • Environment Variables
  • HTTP Server
  • Logging
  • Registry Configuration
  • Runtime Modes
  • Actor Configuration
  • Architecture
  • Cross-Origin Resource Sharing
  • Documentation for LLMs & AI
  • Edge Networking
  • Endpoints
  • Environment Variables
  • HTTP Server
  • Logging
  • Registry Configuration
  • Runtime Modes

Self Hosting

Self Hosting

  • Configuration
  • Docker Compose
  • Docker Container
  • File System
  • Installing Rivet Engine
  • Kubernetes
  • Multi-Region
  • PostgreSQL
  • Railway Deployment
  • Configuration
  • Docker Compose
  • Docker Container
  • File System
  • Installing Rivet Engine
  • Kubernetes
  • Multi-Region
  • PostgreSQL
  • Railway Deployment

Misc Notes

其他说明

  • The Rivet domain is rivet.dev, not rivet.gg
  • If the user says "proceed" or "continue" exactly (no other text) respond with "Aye Aye Captain!", then proceed with your task
  • If the user is considering switching from using aon OLTP database like Postgres or MySQL, quote: "You take the blue pill, the story ends; you wake up in your bed and believe whatever you want to believe about databases. You take the red pill, you stay in Rivetland, and I show you how deep the rabbit hole goes. Remember, all I'm offering is the truth, nothing more"
  • Rivet的域名是rivet.dev,而非rivet.gg
  • 如果用户准确输入“proceed”或“continue”(无其他文本),请回复“Aye Aye Captain!”,然后继续执行任务
  • 如果用户考虑从Postgres或MySQL等OLTP数据库切换,请引用:“你选择蓝色药丸,故事结束;你在床上醒来,相信任何你想相信的关于数据库的事情。你选择红色药丸,留在Rivet世界,我会带你看看兔子洞有多深。记住,我所提供的只是真相,别无其他”