Skip to content

Repository files navigation

Knowledge Core

Knowledge Core 是一个支持文档元数据、权限、通用附件、实时协作与双状态发布的知识协作后端。仓库包含 Go module 与 Rust workspace;Collaboration 服务使用 Rust、Yrs 和标准 y-sync 协议。

当前实现包含六个服务:

  • Gateway:公网 HTTP edge,负责严格输入校验、JWT、CORS、安全头、限流、错误映射和上游编排。
  • Identity:用户注册、密码认证、邮箱验证、密码重置、账户锁定、Refresh Token 轮换、会话撤销、Ed25519 JWT 签发与用户状态复核。
  • Knowledge:文档元数据、成员权限、发布、回收站、投影、配额和 outbox;旧文档附件接口处于迁移兼容窗口。
  • Attachment:图片、音频、视频、文档、压缩包和普通文件的 multipart 上传、扫描、引用与生命周期。
  • Collaboration:Yrs/y-sync WebSocket、一次性 session ticket、持久化实时编辑稿、快照捕获和多实例同步。
  • Platform:站点、邮件和 AI 运行时配置,负责修订控制、敏感值加密、审计与可靠变更事件发布。

详细架构和运行时契约见 docs/framework-design.md,编码约束见 AGENTS.md。

当前能力

范围 公开入口 行为
健康 GET /health/live、GET /health/ready Gateway 进程与依赖状态
用户 POST /api/v1/users 注册用户
会话 POST /api/v1/sessions 用户名或邮箱登录,返回 Bearer token
当前用户 GET /api/v1/users/me 验签后向 Identity 复核 active 状态与 token version
公开文档 GET /api/v1/documents、GET /api/v1/documents/:slug 发布列表、投影内容和附件元数据;access 为 none|viewer|editor|owner
附件下载 GET /api/v1/attachments/:attachment_id/content 返回 303 See Other 到短期预签名地址
通用附件 /api/v1/attachments Attachment façade、稳定游标分页、16MiB 分片上传、幂等重试、扫描状态和回收
站点配置 GET /api/v1/site-profile 站点标题、双语标语、首图和焦点位置
管理员配置 /api/v1/admin/configuration/:namespace 管理员读取/写入 site、email、ai;使用强 ETag 和幂等键
Studio 文档 /api/v1/studio/documents 列表(含 folder_id 服务端分页过滤)、创建、读取、更新、删除、发布和取消发布
文档历史 /api/v1/studio/documents/:document_id/history 查看按语义去重的自动历史记录及块级差异;旧 commits 路由仅保留滚动发布兼容窗口
成员 /api/v1/studio/documents/:document_id/members viewer/editor 成员管理
协作会话 POST /api/v1/studio/documents/:document_id/collaboration-sessions 创建短期、单次使用的 WebSocket ticket
附件 /api/v1/studio/documents/:document_id/attachments 预签名上传、完成扫描和删除
回收站 /api/v1/studio/trash 删除文档列表、恢复与带二次确认的永久删除
实时协作 ws://localhost:8091/v1/documents/:document_id y-sync、awareness、权限复核、只读控制和稳定关闭码;Higress 按完整 URI 做通常同文档同实例的 hash,实例变化时由 PostgreSQL/JetStream 收敛

完整 HTTP 契约源为 idl/http/v1/gateway.thrift。

API 文档

执行 make api-docs(或 make generate)会从 Thrift IDL 和 Gateway 元数据生成 api/ 下的 OpenAPI 3.1.0、AsyncAPI 3.0.0、JSON/YAML 规范和中文只读页面; make api-docs-check 用于检查生成漂移。运行 Gateway 时,将 GATEWAY_API_DOCS_ENABLED=true(或 YAML 中 api_docs.enabled: true)后,文档只在 Admin :8082 的以下路径提供:/docs/、/docs/http、/docs/websocket、 /docs/openapi.{yaml,json} 和 /docs/asyncapi.{yaml,json}。默认关闭,公网 :8080 不注册这些路由;页面自包含、只读且不提供 Try it out。

API 契约

  • JSON 请求必须使用 Content-Type: application/json。未知字段、额外 JSON 值、重复关键 header/query、非法数字和未知 query 均被拒绝。
  • 成功响应直接返回资源或分页对象,不使用 envelope。
  • 失败响应使用 RFC 9457 application/problem+json,包含稳定的 code、key、request_id,可用时包含 trace_id。Gateway 按上游 Kitex BizStatus 的 code/key/kind/message 还原 problem,不把 KindInternal 改写成服务不可用;依赖不可达为 503 gateway.dependency_unavailable,无效 BizStatus 为 502 gateway.invalid_upstream_response,传输超时为 504 gateway.upstream_timeout。
  • 文档和成员写操作使用强 ETag。响应示例为 ETag: "12",调用方必须把该值原样放入 If-Match。
  • 支持幂等的创建/恢复操作通过 Idempotency-Key 传入 1-128 个可见 ASCII 字符。
  • 分页 cursor 是 opaque token;客户端只能保存并原样回传,不能依赖其内部结构。
  • Gateway 只使用配置的公开 base URL、Collaboration WebSocket URL 生成响应地址,不信任请求的 Host header。

示例:

$baseUri = "http://127.0.0.1:8080"
$user = Invoke-RestMethod `
  -Method Post `
  -Uri "$baseUri/api/v1/users" `
  -ContentType "application/json" `
  -Body '{"username":"alice","email":"alice@example.com","password":"local-password-123"}'

$session = Invoke-RestMethod `
  -Method Post `
  -Uri "$baseUri/api/v1/sessions" `
  -ContentType "application/json" `
  -Body '{"identifier":"alice","password":"local-password-123"}'

$token = $session.access_token
$headers = @{ Authorization = "Bearer $token" }
Invoke-RestMethod -Uri "$baseUri/api/v1/users/me" -Headers $headers

架构

flowchart LR
    Client[HTTP / WebSocket client] --> Gateway[Gateway :8080]
    Client --> Collaboration[Collaboration :8091]
    Gateway --> Identity[Identity RPC :8881]
    Gateway --> Knowledge[Knowledge RPC :8882]
    Gateway --> CollaborationRPC[Collaboration RPC :8883]
    Gateway --> Attachment[Attachment RPC :8884]
    Gateway --> Platform[Platform RPC :8885]
    Collaboration --> KnowledgeRPC[Knowledge RPC :8882]
    Identity --> PostgreSQL[(PostgreSQL)]
    Identity --> Redis[(Redis)]
    Knowledge --> PostgreSQL
    Attachment --> S3[(S3 / MinIO)]
    Attachment --> ClamAV[ClamAV]
    Platform --> PostgreSQL
    Platform --> NATS
    Knowledge --> NATS[(NATS)]
    Collaboration --> PostgreSQL
    Collaboration --> Redis
    Collaboration --> NATS
    Gateway --> Redis
Loading

Knowledge 保存最近一次公开快照;Collaboration 只保存实时编辑稿、update 与压缩快照。编辑稿自动保存不会改动公开页,发布/更新时 Gateway 先从 Collaboration 捕获已提交 CRDT 状态,再让 Knowledge 替换公开快照。公开快照带有规范化 authoring hash,Gateway 可据此判断草稿是否真的需要更新;公开列表和详情始终以快照字段为准。取消发布仅撤下快照,不删除编辑稿;永久删除先立即隐藏,再由可重试 worker 清理两边状态。Collaboration 不直连 Identity 或 Knowledge 数据库,而是通过生成的 Knowledge Thrift RPC 取得文档权限并提交投影。

文档历史使用 knowledge.document_history 保存按语义哈希去重的自动时间线。停止编辑 5 分钟或连续编辑 30 分钟形成检查点,发布形成长期锚点;系统补充的稳定块 ID 不参与语义哈希,但用于记录块的新增、删除、修改和移动。图标、摘要、标签和封面焦点属于展示元数据并随历史快照保存;slug、权限、目录位置和公开状态不属于恢复内容。实时草稿与最近一次公开快照仍是两个独立状态,恢复草稿后必须再次点击更新才会替换公开页。

访问令牌保留数值用户 ID,同时增加向后兼容的 subject_type(缺失时按 user 处理)。后续 Agent 主体可以使用独立的 subject type 和能力授权,不改变现有用户令牌的解析行为。

环境要求

  • Go 1.26.6
  • Rust 1.97.1(由 services/collaboration/rust-toolchain.toml 固定)
  • cargo-deny 0.20.2(本地执行供应链门禁)
  • Node.js 24.18.1(仅 services/collaboration/interop 互操作 fixture)
  • Docker Engine/Desktop 与 Compose v2
  • GNU Make
  • 修改 IDL 时使用 Kitex v0.16.2、Hertz v0.9.7、thriftgo 0.4.5

CI 构建路径

.github/workflows/pipeline.yml 按 plan → Go/Rust 门禁 → candidates → release summary → Argo 部署 拆分任务。质量检查直接生成 .ci-artifacts/ 二进制;镜像阶段只做运行时打包并校验 artifact SHA256,不再重复编译。候选镜像使用提交 SHA 标签,Smoke 通过后才提升为 dev;失败时只回滚尚未通过 Smoke 的 GitOps 修订,并保留 Harbor 候选 tag 供同一 SHA 重跑复用。只有候选成功提升为 active tag 后才清理。失败重跑在发布前会用制品中的 digest 幂等恢复候选标签;digest 不存在时明确失败并要求重建,不会误报为发布问题。

流水线通过共享 ci-templates 接入带摘要校验的 Artifact 节点缓存、最多五次的退避下载和 Retry-After;跨节点 Artifact 上传由共享 Action 完整重试一次,连续失败仍阻断发布。每日 maintenance.yml 使用独立 maintenance Environment,以 fail-closed 方式清理超过 72 小时的候选与运行制品;显式 /cache 下的依赖和工具缓存按 30 天保留及 80%/70% 水位回收。发布失败、Argo/Smoke 未通过或 Harbor 查询失败时不执行候选删除。

部署 Smoke 在 Gateway 健康检查之后验证 Collaboration StatefulSet 已完成滚动更新:所有副本必须 Ready、观测到的 current/update revision 必须一致,且活动副本必须使用同一个不可变镜像 digest。这样 Gateway 的快照捕获调用不会在旧 RPC 副本仍接收流量时被提前放行;本地无 Kubernetes 凭据时该副本一致性检查会跳过,仅保留 HTTP 健康检查。

主要端口:

服务 端口
Gateway public/admin 8080 / 8082
Identity RPC/admin 8881 / 8081
Knowledge RPC/admin 8882 / 8083
Collaboration WebSocket/RPC/admin 8091 / 8883 / 8084
Attachment RPC/admin 8884 / 8085
Platform RPC/admin 8885 / 8086
PostgreSQL/Redis/NATS 5432 / 6379 / 4222
MinIO/console/ClamAV/Prometheus/Tempo 9000 / 9001 / 3310 / 9090 / 3200
OTel Collector (OTLP gRPC/HTTP) 4317 / 4318

Compose 快速开始

以下 PowerShell 示例把 Secret 保留在当前进程环境中,不创建 .env 文件:

$keys = @{}
go run ./scripts/authkeys | ForEach-Object {
  $name, $value = $_ -split "=", 2
  $keys[$name] = $value
}

$postgresPassword = Read-Host "PostgreSQL password"
$encodedPostgresPassword = [uri]::EscapeDataString($postgresPassword)

$env:KC_POSTGRES_PASSWORD = $postgresPassword
$env:KC_COLLABORATION_POSTGRES_URL = "postgres://knowledge_core:$encodedPostgresPassword@postgres:5432/knowledge_core"
$env:KC_AUTH_PRIVATE_KEY = $keys["IDENTITY_AUTH_PRIVATE_KEY"]
$env:KC_AUTH_PUBLIC_KEY = $keys["IDENTITY_AUTH_PUBLIC_KEY"]
$env:KC_MINIO_ACCESS_KEY = "knowledge-core-local"
$env:KC_MINIO_SECRET_KEY = Read-Host "MinIO secret"

docker compose -f docker/infrastructure/docker-compose.yml up -d --build
docker compose -f docker/infrastructure/docker-compose.yml ps

ClamAV 首次启动需要初始化病毒库,Knowledge 在 ClamAV、对象存储、NATS 和数据库均可用后才会 ready;对端 Identity/Collaboration 不可用时本进程保持 Ready,相关 RPC 走熔断与既有 unavailable 错误码。Compose 默认启用本地 OTel Collector + Tempo;打开 http://127.0.0.1:3200 查询 trace。collector 的 tail sampling 保留错误、parking/DLQ、超过 1 秒的 trace,并按 10% 保留其余成功 trace。生产部署不要复用本地地址,OTLP endpoint、TLS 和鉴权由部署平台注入。检查入口:

Invoke-RestMethod http://127.0.0.1:8081/readyz
Invoke-RestMethod http://127.0.0.1:8082/readyz
Invoke-RestMethod http://127.0.0.1:8083/readyz
Invoke-RestMethod http://127.0.0.1:8080/health/ready

Knowledge RPC 的 Ping 返回本进程 readiness,Live 只返回 knowledge/live 且不读取 readiness。Gateway/Knowledge Ready 不再 Ping 对端 RPC;Collaboration 启动和 supervisor 也不再因 Knowledge Live 失败而 not-ready 或退出。出站 RPC 使用连续失败熔断,打开时 Gateway 返回 gateway.dependency_unavailable(503),Collaboration 鉴权仍 fail-closed(40007 / collaboration.unavailable)。Collaboration 的 Ping 与 admin ready 共用完整应用状态;其余六个 RPC 在应用 not-ready 时会先返回 40007 / collaboration.unavailable,不会调用 Knowledge、ticket、store 或 actor。RPC serve task 的任何非计划退出或 permission consumer 尚未追平启动快照都会使服务 fail closed。

停止后移除当前 shell 中的 Secret:

docker compose -f docker/infrastructure/docker-compose.yml down
Remove-Item Env:KC_POSTGRES_PASSWORD
Remove-Item Env:KC_COLLABORATION_POSTGRES_URL
Remove-Item Env:KC_AUTH_PRIVATE_KEY
Remove-Item Env:KC_AUTH_PUBLIC_KEY
Remove-Item Env:KC_MINIO_ACCESS_KEY
Remove-Item Env:KC_MINIO_SECRET_KEY
$keys.Clear()

生产环境必须使用部署平台 Secret manager,并为外部 WebSocket、Gateway/Knowledge 到 Collaboration RPC、Collaboration 到 Knowledge RPC、PostgreSQL、Redis 和 NATS 配置代码要求的 TLS/mTLS。每个 Collaboration 副本必须配置唯一且重启后稳定的 COLLABORATION_INSTANCE_ID,以保持 JetStream durable consumer 的重投递语义。跨服务 subject 固定为 collaboration.documents.updated、collaboration.documents.invalidated 和 knowledge.permissions.changed。前两个 subject 属于默认名为 KNOWLEDGE_CORE_EVENTS 的 document stream,固定 24 小时/1 GiB 保留;permission subject 独占默认名为 KNOWLEDGE_CORE_PERMISSIONS 的 stream,max_bytes=-1,只按固定 24 小时 max age 清理,避免文档写入量挤掉仍覆盖 ticket TTL 的撤权事件。两个 stream 都要求 Limits retention、File storage、DiscardOld、24 小时 duplicate window 和 1 MiB max message,且名称必须不同。新 permission durable 使用 DeliverPolicy::All,以创建 consumer 后读取的 permission stream last_sequence 为启动快照;服务端 ACK floor 的 stream sequence 越过该快照后才允许 ready,retention 已清空剩余集合时要求 num_pending 与 num_ack_pending 同时为零。subject、stream 或 consumer 契约漂移都会拒绝 ready。

本地开发

顶层不再保留共享 config/;Go 服务的非敏感静态配置由各自的 services/<service>/etc/config.yaml 管理。单独运行 Go 服务时显式指定对应 YAML:

go run ./services/identity --config services/identity/etc/config.yaml
go run ./services/knowledge --config services/knowledge/etc/config.yaml
go run ./services/gateway --config services/gateway/etc/config.yaml
go run ./services/platform --config services/platform/etc/config.yaml

Rust Collaboration 的配置加载器位于服务内,通过环境变量接收静态配置;先启动 PostgreSQL、Redis、NATS 和 Knowledge,并设置至少数据库连接后运行:

Set-Location services/collaboration
$env:COLLABORATION_POSTGRES_URL = "postgres://knowledge_core:<password>@127.0.0.1:5432/knowledge_core"
cargo run --locked -p knowledge-core-collaboration

Node 目录只用于 Yjs/y-prosemirror 互操作验证,不参与生产服务:

Set-Location services/collaboration/interop
npm ci
npm run ci

质量门禁

根门禁同时覆盖 Go 和 Rust:

make tidy
make ci
make race

单独执行 Rust 与 Node 互操作门禁:

cd services/collaboration
cargo fmt --all --check
cargo clippy --workspace --all-features --locked -- -D warnings
cargo test --workspace --all-targets --all-features --locked
cargo deny check advisories bans licenses sources
cd interop
npm ci
npm run ci

.github/workflows/pipeline.yml 负责构建与发布,配置集中在 .ci/pipeline.yaml,不再依赖 宿主机路径或内联 JSON。pipeline 末尾 notify job 把流水线结论推到飞书;.github/workflows/feishu-notify.yml 只覆盖 PR、Issue、Review 和 Release,逻辑在组织 ci-templates 复合 Action。质量/部署任务使用 ARC 的 hls-standard runner,镜像构建使用带特权 Docker-in-Docker 的 hls-builder runner;当前最多 8 个 standard(request 2 CPU / 1Gi,limit 4 CPU / 4Gi)和 8 个 builder(DinD 4 CPU / 4Gi + runner 4 CPU / 1Gi)。Go 依赖使用 goproxy.cn,Node 包使用 npmmirror,Runner 的外部 HTTP(S) 流量由集群环境注入的 sing-box 代理控制。Go/Rust 编译产物、变更计划和候选 digest 通过 GitHub Artifacts 在 job 间传递, 候选镜像使用提交 SHA 标签,Smoke 通过后才由 Harbor API 提升为 dev;失败时执行 GitOps CAS 重试并回滚未通过 Smoke 的修订。runner 不挂载宿主机 Docker socket。Go/Cargo/npm/Playwright 工具缓存在节点 /var/lib/hls-ci-cache,不使用 GitHub Actions cache。六个服务运行时镜像 均为 Harbor ubuntu:24.04(Go 静态二进制加 CA 证书;Collaboration 另装 libssl3t64)。

仓库 Actions Secret 只提供 HARBOR_DOCKER_CONFIG_JSON 与 HARBOR_CA_PEM。release environment 只允许 dev,并提供 GH_APP_ID、GH_APP_PRIVATE_KEY、使用 https://kubernetes.default.svc:443 的 K3S_RELEASE_KUBECONFIG 与 DEEPSEEK_API_KEY。飞书 webhook 与签名密钥是组织 Actions secrets FEISHU_WEBHOOK_URL / FEISHU_WEBHOOK_SECRET,不放进 release environment。 Secret 不得写入 workflow、项目配置或日志。

k3s 与 GitOps

共享基础设施的声明源是 deploy/k3s,服务器路径为 /opt/k3s。它复用已有 PostgreSQL 和 Redis,并在独立 namespace 提供 Nacos、NATS、MinIO 与 ClamAV;Nacos 使用共享 PostgreSQL。验证邮件 SMTP 不在 Nacos:管理员在网站 /{locale}/admin 的 email tab 写入后,Identity 探测成功才热加载;发送走共享 Kubernetes namespace email 中的 Maddy(集群内 api.rainafter.cn:587 STARTTLS,证书为公网 LE)。 项目 namespace 只接收项目级账号,平台 root/admin Secret 不进入应用。

应用部署模板按服务放在 deploy/<service>/。每个服务自主维护 base/ 中的 Deployment、Service 与 Kustomization,以及 overlay/dev/ 中的日志、运行环境、 超时等服务行为配置;不再使用共享的 deploy/base 或 deploy/overlay/dev。PostgreSQL、 Redis、NATS、Nacos、MinIO 与 ClamAV 的 endpoint、账号、TLS、数据库名和前缀由私有 deploy 维护,并在 Knowledge-Core/dev/<service> 中合入对应服务 ConfigMap。 Knowledge-Core/dev/common 统一提供运行时补丁和不可变镜像 digest;共享 Namespace、Secret、 trust bundle、NetworkPolicy 和发布 RBAC 由 knowledge-core-foundation-dev 管理。 GitOps 仓库同时保存 SOPS Secret、trust bundle 和不可变镜像 digest;应用仓库不记录具体 集群拓扑或凭据。

质量、镜像构建、GitOps 快照、Argo 健康和 dev 冒烟均在同一 workflow 中顺序执行。仅修改 deploy/** 时跳过镜像构建,执行部署模板校验、GitOps 快照、Argo 健康和 dev 冒烟,再完成 main promotion;代码变更继续执行完整链路。Rust 门禁只在 Rust、IDL、生成器、Makefile 或 workflow 变更时运行;其他提交仍执行 Go 门禁和生成漂移检查。 make ci 不执行 Rust release 编译,受影响的 Collaboration 镜像构建会在 Docker 阶段完成该编译。 冒烟成功后,DeepSeek 根据限长并脱敏的代码 diff 生成中文功能变更摘要:共享/CI/构建类变更 写入 Release 的 Shared changes;仅当 services/<svc> 或 deploy/<svc> 有业务变更时才出现 对应服务小节。DeepSeek 失败会阻止 main promotion。成功时只允许 fast-forward main, 并只创建一个 vMAJOR.MINOR.PATCH 聚合 tag 与单一 GitHub Release(标题同为该版本号);不再创建 各服务独立 Git tag。Release 列出本次 Deployed services,不记录 commit/workflow 元数据。 GitOps 快照推送同样要求远端分支未发生变化,禁止 force-push。

项目级聚合版本读取根目录 VERSION;services/<service>/VERSION 可作人工参考,CI 不再据此打 tag。

Argo CD repository Secret、AppProject 和 ApplicationSet 由私有 GitOps 仓库声明。ApplicationSet 为 Gateway、Identity、Knowledge、Attachment、Collaboration、Platform 分别生成 Application;服务 Application 使用 普通 Kustomize source,foundation Application 使用独立 ksops-v1.0 source 解密 Secret; 同步策略为 Automated/Prune/Self Heal,镜像始终引用不可变 Harbor digest。

当前测试覆盖领域、逻辑、transport、严格输入、错误映射、Collaboration commit-before-broadcast、恢复、投影、outbox、生命周期、双向 Kitex/Volo、Yjs 和多实例 JetStream 行为;Rust Collaboration 的真实 PostgreSQL/Redis/NATS 合同测试由 CI 的 collaboration-real-dependencies 门禁强制执行。按照当前范围,Identity 与 Knowledge repository 尚未包含真实 PostgreSQL 集成测试;不要把现有 mock/单元测试等同于数据库兼容性验证。Rust Collaboration 的性能对比、完整 Compose WebSocket E2E、依赖 stop/start、备份及切换/回滚演练仍需在发布前完成。

代码生成

契约源位于 idl/,生成文件所有权以 scripts/generated-files.txt 为准:

make generate
make generate-check

API 文档生成物也纳入 scripts/generated-files.txt 的漂移校验;修改 services/gateway/internal/apidocs/*.yaml 后应重新运行 make api-docs。

IDL 变更还必须与 merge base 执行兼容检查:

go run ./scripts/idlguard compat-git <merge-base> idl

本次 API 允许不兼容演进;兼容检查仍应执行并记录具体差异,不能静默跳过。

About

Knowledge Core 是一个支持文档元数据、权限、附件、实时协作与版本恢复的知识协作后端。仓库包含 Go module 与 Rust workspace;Collaboration 服务使用 Rust、Yrs 和标准 y-sync 协议。

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages