加载中...
OleanderChat 是 OleanderStudio 推出的旗舰产品,基于 Kotlin Multiplatform 和 Compose Multiplatform 构建的跨平台聊天应用。采用 RSA-2048 + AES-256-GCM 混合加密方案,确保您的每一次对话都安全无忧。后端可自托管到 Cloudflare Workers,完全掌控数据。
支持 Android 和桌面端(JVM),一套代码,多端运行。
获取源码OleanderChat 提供完整的即时通讯解决方案,从加密到文件分享,一应俱全。
RSA-2048 + AES-256-GCM 混合加密,每条消息独立加密,私钥仅存储在本地。
基于 WebSocket 连接的即时通讯,消息实时送达,支持未读消息徽章。可部署到自己的 Cloudflare Worker。
使用 UUID 添加好友,发送和接受好友请求,管理您的好友列表。
通过 EasySend 集成上传和分享文件,文件元数据加密传输,双方均可解密。
聊天记录本地保存,跨会话保留,长按即可删除聊天记录。
支持 Android 和桌面端(JVM),基于 Material Design 3 的现代化 UI。
首次启动时,每个用户都会获得唯一的 RSA 密钥对(公钥 + 私钥)。
发送消息或好友请求时自动交换公钥,私钥永远不会传输到服务器。
每条消息生成随机 AES-256 密钥,使用 AES-256-GCM 加密内容,再用接收方 RSA 公钥加密 AES 密钥。
只有预期接收方才能使用私钥解密消息,确保端到端的安全性。
OleanderChat 采用现代化的技术架构,确保性能和体验。
跨平台共享业务逻辑
声明式 UI 框架
实时通讯连接
混合加密方案
OleanderChat 的后端基于 Cloudflare Workers 构建,使用 Durable Objects 实现 WebSocket 消息路由。您可以免费部署到自己的 Cloudflare 账户,完全掌控数据。
单文件 Worker 脚本,无需数据库,UUID 即身份标识。
Cloudflare Workers 免费额度足以支撑个人和小团队使用。
消息仅作为传输中继,不存储在服务器端,端到端加密确保内容安全。
只需几步即可部署和运行 OleanderChat。
将 Cloudflare Worker 脚本部署到您的账户,获取 WebSocket 地址。
从 GitHub 克隆 OleanderChat 客户端源码到本地。
配置 WebSocket 地址,使用 Gradle 运行 Android 或桌面端应用。
分享您的 UUID,添加好友,开始端到端加密聊天。
以下是可以直接部署到 Cloudflare Workers 的完整后端代码。
import { DurableObject } from "cloudflare:workers";
class UUIDRouter extends DurableObject {
constructor(ctx, env) {
super(ctx, env);
this.ctx.setWebSocketAutoResponse(
new WebSocketRequestResponsePair("ping", "pong")
);
}
async fetch(request) {
const url = new URL(request.url);
const clientUUID = url.searchParams.get("uuid");
const pair = new WebSocketPair();
const [client, server] = Object.values(pair);
this.ctx.acceptWebSocket(server, [clientUUID]);
server.serializeAttachment({ uuid: clientUUID });
return new Response(null, { status: 101, webSocket: client });
}
async webSocketMessage(ws, message) {
const senderAtt = ws.deserializeAttachment();
const senderUUID = senderAtt?.uuid;
let msg;
try {
msg = JSON.parse(message);
} catch {
ws.send(JSON.stringify({ error: "Invalid JSON" }));
return;
}
const targetUUID = msg.UUID接收;
const payload = msg.message;
if (!targetUUID) {
ws.send(JSON.stringify({ error: "Missing UUID接收" }));
return;
}
const out = JSON.stringify({ [senderUUID]: payload });
const target = this.ctx.getWebSockets(targetUUID)
.find((w) => w.deserializeAttachment()?.uuid === targetUUID);
if (target) {
target.send(out);
} else {
ws.send(JSON.stringify({ error: `UUID ${targetUUID} not online` }));
}
}
async webSocketClose(ws, code, reason, wasClean) {
ws.close(code, reason);
}
async webSocketError(ws, error) {
console.error("ws error", error);
}
}
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
if (url.pathname !== "/ws") {
return new Response("Supported endpoints: /ws?uuid=xxx", { status: 200 });
}
if (request.headers.get("Upgrade") !== "websocket") {
return new Response("Expected Upgrade: websocket", { status: 426 });
}
const uuid = url.searchParams.get("uuid");
if (!uuid) {
return new Response("Missing uuid query param", { status: 400 });
}
const id = env.UUID_ROUTER.idFromName("global-router");
const stub = env.UUID_ROUTER.get(id);
return stub.fetch(request);
}
};
部署步骤:
UUID_ROUTERwss://your-worker.your-subdomain.workers.dev/ws?uuid=YOUR_UUID 连接