@ arbidocs/ 客户端
ARBI API的TypeScript客户端——零知识认证,E2E加密,以及类型安全的REST客户端.
安装
npm install @arbidocs/client
对于Node.js环境(没有浏览器),也安装了索引DB多填充:
npm install fake-indexeddb
快速启动
import { createArbiClient } from '@arbidocs/client'
const arbi = createArbiClient({
baseUrl: 'https://www.arbidocs.com',
deploymentDomain: 'www.arbidocs.com',
})
// Login
const session = await arbi.auth.login({
email: '[email protected]',
password: 'mypassword',
})
// Type-safe API calls — auto-injects Bearer token
const { data: workspaces } = await arbi.fetch.GET('/api/user/workspaces')
console.log(workspaces) // WorkspaceResponse[]
实例
注册+登录(节点.js)
// Polyfill IndexedDB for Node.js (must be first import)
require('fake-indexeddb/auto')
const { createArbiClient } = require('@arbidocs/client')
async function main() {
const arbi = createArbiClient({
baseUrl: 'https://www.arbidocs.com',
deploymentDomain: 'www.arbidocs.com',
credentials: 'omit', // No cookies in Node.js
})
// Register a new user
const { signingPrivateKey } = await arbi.auth.register({
email: '[email protected]',
password: 'SecurePass123!',
verificationCode: '...', // From /api/user/verify-email flow
firstName: 'Alice',
lastName: 'Smith',
})
// Login
const session = await arbi.auth.login({
email: '[email protected]',
password: 'SecurePass123!',
})
console.log('Logged in as', session.userExtId)
console.log('Token:', session.accessToken.slice(0, 20) + '...')
}
main()
创建工作空间并上传文档
require('fake-indexeddb/auto')
const fs = require('fs')
const {
createArbiClient,
getSession,
sealedBoxDecrypt,
deriveEncryptionKeypairFromSigning,
createWorkspaceKeyHeader,
} = require('@arbidocs/client')
async function main() {
const arbi = createArbiClient({
baseUrl: 'https://www.arbidocs.com',
deploymentDomain: 'www.arbidocs.com',
credentials: 'omit',
})
// Login
const { accessToken, serverSessionKey } = await arbi.auth.login({
email: '[email protected]',
password: 'SecurePass123!',
})
// Create workspace
const { data: workspace } = await arbi.fetch.POST('/api/workspace/create_protected', {
body: {
name: 'My Case Files',
description: 'Documents for Case #1234',
is_public: false,
},
})
// Decrypt the workspace key and generate the auth header.
// The server wraps the workspace symmetric key with your public key
// during creation. You unwrap it here to prove you own the workspace.
const session = await getSession()
const pubKey = session.signingPrivateKey.slice(32, 64)
const encKP = deriveEncryptionKeypairFromSigning({
publicKey: pubKey,
secretKey: session.signingPrivateKey,
})
const workspaceKey = sealedBoxDecrypt(workspace.wrapped_key, encKP.secretKey)
const wsHeader = await createWorkspaceKeyHeader(workspaceKey, serverSessionKey)
// Tell the SDK which workspace is active (middleware auto-injects the header)
arbi.session.setSelectedWorkspace(workspace.external_id)
arbi.session.setCachedWorkspaceHeader(workspace.external_id, wsHeader)
// Upload a PDF (multipart — use raw fetch since openapi-fetch
// doesn't handle multipart/form-data)
const formData = new FormData()
const pdf = fs.readFileSync('./contract.pdf')
formData.append('files', new Blob([pdf], { type: 'application/pdf' }), 'contract.pdf')
const uploadRes = await fetch(
`https://www.arbidocs.com/api/document/upload?workspace_ext_id=${workspace.external_id}`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'workspace-key': wsHeader,
},
body: formData,
}
)
const { doc_ext_ids } = await uploadRes.json()
console.log('Uploaded document:', doc_ext_ids[0])
// List documents (workspace-key header injected automatically by middleware)
const { data: docs } = await arbi.fetch.GET(
'/api/workspace/{workspace_ext_id}/documents',
{ params: { path: { workspace_ext_id: workspace.external_id } } }
)
for (const doc of docs) {
console.log(` ${doc.file_name} — ${doc.status}`)
}
}
main()
订阅会话状态更改
SDK的车 SessionManager 支持被动更新的订阅模式:
const arbi = createArbiClient({ baseUrl, deploymentDomain })
// React to session changes (useful for custom UI frameworks)
const unsubscribe = arbi.session.subscribe((state) => {
console.log('Session changed:', {
loggedIn: !!state.accessToken,
email: state.userEmail,
workspace: state.selectedWorkspaceId,
})
})
// Later: stop listening
unsubscribe()
API 参考
createArbiClient(options)
创建 SDK 客户端实例 .
interface ArbiClientOptions {
baseUrl: string // ARBI API URL (e.g. "https://www.arbidocs.com")
deploymentDomain: string // Used for deterministic key derivation
credentials?: RequestCredentials // Default: 'include'. Use 'omit' for Node.js
session?: SessionManager // The host's session state; default: in-memory
persistence?: SessionPersistence // Keys between page loads; default: encrypted IndexedDB
sso?: SsoTokenSource | null // The SSO provider's token, asked only in SSO mode
middleware?: Middleware[] // Host middleware, run before the re-login middleware
reloginExcludePatterns?: string[] // Paths whose 401 never re-logs in
onReloginSuccess?: (data) => void
onRetryCloneFailed?: () => void
}
客户端拥有会话回收: 每一次请求失败, 都会重写一次 401次
(单机,5s冷却),400"无活工作空间"重新打开工作空间,并重新登录
服务器拒绝结束会话( q)session.clear(),则 auth.onSessionExpired ) (中文(简体) ). A 类
登录仍可完成或无法访问的服务器会让会话单独离开 .
返回 a ArbiClient 带有这些命名空间 :
| 命名空间 | 说明 |
|---|---|
arbi.fetch | 类型安全 openapi-fetch 带有自动自动中间软件的客户端 |
arbi.session | 记忆会话状态( 地址、 用户、 工作空间) |
arbi.auth | 高级认证 : register, (中文(简体) ), login, (中文(简体) ), loginWithKey, (中文(简体) ), logout, (中文(简体) ), changePassword, (中文(简体) ), relogin |
arbi.crypto | 加密工具:密钥衍生、签名、加密、基础64 |
arbi.storage | 索引数据库会话持久性 : getSession, (中文(简体) ), saveSession |
认证方法
// Register
await arbi.auth.register({ email, password, verificationCode, firstName?, lastName? })
// Login with password
await arbi.auth.login({ email, password })
// Login with recovery key (Uint8Array)
await arbi.auth.loginWithKey({ email, signingPrivateKey })
// Change password
await arbi.auth.changePassword({ email, currentPassword, newPassword })
// Logout (clears session + IndexedDB)
await arbi.auth.logout()
// Re-login using stored private key (auto-called by middleware on 401)
await arbi.auth.relogin()
// "The session could not be recovered": show the sign-in screen
const off = arbi.auth.onSessionExpired(() => showSignIn())
// Seal every workspace key to this server session (before deriving an MCP session from it)
await arbi.auth.openAllWorkspaces()
中件( 自动应用)
这个 arbi.fetch 客户端自动应用了三个中间软件层:
- 熊熊奥特 - 注射剂
Authorization: Bearer <token>在每次请求中 - 工作空间密钥 - 注射剂
workspace-key工作空间范围终点页眉 - 自动重写 401年,从所储存的钥匙和回盘中重新提取证书
独立出口
所有构件可单独导入供高级使用:
import {
// Crypto primitives
initSodium, signMessage, sealedBoxDecrypt, sealedBoxEncrypt,
deriveEncryptionKeypairFromSigning, createWorkspaceKeyHeader,
generateUserKeypairs, generateLoginCredentials,
encryptMessage, decryptMessage,
base64Encode, base64Decode, base64ToBytes, bytesToBase64,
// Storage
getSession, saveSession, clearAllData, hasSession,
// Middleware (compose your own client)
createBearerAuthMiddleware, createWorkspaceKeyMiddleware, createAutoReloginMiddleware,
// Relogin handler
createReloginHandler,
// Session manager
createSessionManager,
// OpenAPI schema types
type paths, type components, type operations,
} from '@arbidocs/client'
锁定器
import { configureLogger, logger } from '@arbidocs/client'
configureLogger({ verbose: import.meta.env.DEV }) // once, at startup
logger.error('always printed')
logger.warn('printed only when verbose') // likewise info, debug
如何运作
零知识认证
ARBI使用Ed25519签名进行认证. 您的密码从不离开客户端 :
- 密钥衍生数字 :
Argon2id(email + password + deploymentDomain)* Ed25519种子* - 登记: 发送到服务器的公钥, 私钥留在本地
- 登录:客户端签署时间戳,服务器验证签名
- 会议: 服务器返回用于工作空间操作的加密会话密钥
工作空间加密
每个工作区都有一个对称的密钥(NaCl SecretBox). 关键是:
- 创建工作空间时生成的服务器侧
- 与您的 X25519 公用密钥相接( 来源于您的 Ed25519 签名密钥)
- 使用未包装客户端
sealedBoxDecrypt - 用于生成每个请求
workspace-key头(HMAC钥匙持有证明)
索引 DB 存储
SDK将签名的私人密钥和会话密钥存储在索引DB中,用从Web Crypto API中无法提取的AES-GCM密钥加密. 在节点.js中,使用 fake-indexeddb 作为多填充.
所需资源
- 浏览器: 任何现代浏览器( Chrome, Firefox, Safari, Edge)
- 节点.js: v18+ (需要数)
fetch, (中文(简体) ),FormData, (中文(简体) ),Blob(原始内容存档于2017-09-29). Globals. 安装fake-indexeddb用于索引数据库. - 类型脚本: 5.0+(可选——也与普通联署材料合作)
释放
每个版本的放出注释 GitHub 发布。 。 。 .