中文 · Concepts · Strategy · Contributing
Standardized Programmable Ontology Knowledge Engine — a protocol of JSON Schema wire contracts for narrative KnowledgeEntry data and ops. Independent products exchange consistency-check and context-assembly I/O through these shapes.
Includes:
- Data-layer schemas: KnowledgeEntry, Relation, SourceAnchor, Finding, AssemblePacket, HostCapabilityManifest, Rule, TimelineEvent, MindState
- Ops-layer schemas:
upsert, extract→promote,relate,check,assemble; optionalproject/computeunderl2-computable - Generated TypeScript (
@42ch/spoke-schemas) and Rust (spoke-schemas,spoke-operations) - Pure lifecycle helpers plus adapter ports and injection orchestration (
@42ch/spoke-operations/spoke-operations) - Opt-in Connect for signed cross-process interaction (
@42ch/spoke-connect/spoke-connect, plus native bindings, including C and C++) - Protocol conformance fixtures and reference
ToyWorldAdapter(fixtures/toy-world/)
Published consumer packages share one lockstep SemVer.
| Package | Registry | Role |
|---|---|---|
@42ch/spoke-schemas |
npm | Generated TypeScript wire types — what crosses the wire |
@42ch/spoke-operations |
npm | Pure helpers, adapter ports, and orchestration over those types |
@42ch/spoke-connect |
npm | Opt-in connect client — identity, hello auth, session core, WebSocket transport |
spoke-schemas |
crates.io | Generated Rust wire types |
spoke-operations |
crates.io | Pure helpers, adapter ports, and orchestration — parity with @42ch/spoke-operations |
spoke-connect |
crates.io | Opt-in connect reference — libp2p transport, session core, uniffi binding surface |
Product-specific payloads live under extensions.<namespace> (namespace keys are product-chosen ids). Cross-product functional dialects shared by narrative hosts (lore activation, knowledge packs, assemble placement) live under modules.* — an optional, capability-flagged bag on KnowledgeEntry, AssemblePacket, and TimelineEvent. See Data model.
Install pins and package roles: Package quick-start.
pnpm add @42ch/spoke-schemas @42ch/spoke-operations
# Pin both to the same lockstep SemVer, e.g. @X.Y.Z@42ch/spoke-schemas — import generated wire types:
import type {
KnowledgeEntry,
TimelineEvent,
PromoteRequest,
AssemblePacket,
HostCapabilityManifest,
} from "@42ch/spoke-schemas";@42ch/spoke-operations — implement capability-sliced ports on one adapter, then call orchestrate*:
import type { PromoteRequest, UpsertRequest } from "@42ch/spoke-schemas";
import {
orchestrateUpsert,
orchestratePromote,
orchestrateCheck,
orchestrateAssemble,
type BaselineAdapter,
} from "@42ch/spoke-operations";
declare const adapter: BaselineAdapter; // product implements BaselineAdapter / FullAdapter
declare const upsertRequest: UpsertRequest;
declare const promoteRequest: PromoteRequest;
async function runBaseline() {
const upserted = await orchestrateUpsert(adapter, upsertRequest);
const promoted = await orchestratePromote(adapter, promoteRequest);
}Optional capabilities use ComputableAdapter / ForkAdapter (or FullAdapter) with orchestrateProject, orchestrateCompute, orchestrateForkCheck, and orchestrateForkAssemble. Pure helpers (validatePromoteRequest, mergeExtensionMaps, buildAssemblePacket, …) remain available for focused gates.
cargo add spoke-schemas spoke-operations
# Pin both to the same lockstep SemVer, e.g. X.Y.Z# Cargo.toml
[dependencies]
spoke-schemas = "X.Y.Z"
spoke-operations = "X.Y.Z"spoke-schemas — wire types from the same JSON Schema SSOT:
use spoke_schemas::{KnowledgeEntry, HostCapabilityManifest, PromoteRequest, TimelineEvent};spoke-operations — port traits plus orchestrate_* (also re-exports spoke_schemas):
use spoke_operations::{
orchestrate_promote, orchestrate_upsert, BaselineAdapter,
};
use spoke_operations::spoke_schemas::{PromoteRequest, UpsertRequest};
async fn run_baseline(adapter: &impl BaselineAdapter, upsert: UpsertRequest, promote: PromoteRequest) {
let _ = orchestrate_upsert(adapter, upsert).await;
let _ = orchestrate_promote(adapter, promote).await;
}Declare the spoke-connect capability when hosts need signed cross-process interaction (hello, session, invoke, auth envelopes).
TypeScript — npm client with WebSocket transport:
pnpm add @42ch/spoke-connect
# Pin to the same lockstep SemVer as schemas / operationsRust — crates.io reference with libp2p transport and a uniffi binding surface for other host languages:
cargo add spoke-connectC and C++ — git-based from the release tag: check out vX.Y.Z and take crates/spoke-connect/bindings/cpp/include/spoke_connect.h (hand-written C ABI header), crates/spoke-connect/bindings/cpp/include/spoke_connect.hpp (C++17 header-only convenience layer), and the committed carrier for your target — crates/spoke-connect/bindings/cpp/native/osx-arm64/libspoke_connect_capi.dylib for macOS arm64 or crates/spoke-connect/bindings/cpp/native/win-x64/spoke_connect_capi.dll for Windows x64. Both carrier libraries are Git LFS objects, so run git lfs install once and git lfs pull in an existing clone before linking.
git clone --branch vX.Y.Z --depth 1 https://github.com/42ch-dev/spoke.gitConnect is multi-language: a language-native TypeScript client on npm, a Rust reference on crates.io (libp2p + uniffi binding surface), and native bindings — registry-backed for C# (GitHub Packages NuGet), Kotlin (GitHub Packages Maven), and Python (PyPI), git-based for Swift, Go, and C/C++ (committed headers and platform carriers). Guides: TypeScript client, native bindings, and Connect from C and C++.
Pin every consumer surface to the same SemVer (X.Y.Z) on npm and crates.io:
pnpm add @42ch/spoke-schemas@X.Y.Z @42ch/spoke-operations@X.Y.Z
cargo add spoke-schemas@X.Y.Z spoke-operations@X.Y.ZAnnotated git tags vX.Y.Z match that lockstep version. Release notes: CHANGELOG.md and GitHub Releases. Pinning guide: Version & release.
Implement the port families for the capabilities you claim on one adapter type, then call orchestrate* from @42ch/spoke-operations (Rust: orchestrate_*).
import type { KnowledgeEntry, PromoteRequest } from "@42ch/spoke-schemas";
import {
orchestratePromote,
type BaselineAdapter,
} from "@42ch/spoke-operations";
// Product adapter implements BaselineAdapter.
// Reference FullAdapter: fixtures/toy-world ToyWorldAdapter
declare const adapter: BaselineAdapter;
const candidate: KnowledgeEntry = {
schema_version: 1,
entry_id: "kb_01",
entry_type: "character",
canonical_name: "Aria",
status: "provisional",
body: { summary: "A reluctant scout." },
extensions: {},
};
const request: PromoteRequest = { candidate };
async function promote() {
const result = await orchestratePromote(adapter, request);
if (result.ok) {
// Confirmed entry persisted through adapter OCC ports
} else {
console.error(result.code, result.message);
}
}Reference FullAdapter and the committed “Mira at Harbor” graph: fixtures/toy-world/. Step-by-step package path: Package quick-start.
| Term | In SPOKE |
|---|---|
| KnowledgeEntry | Atomic narrative knowledge unit on the wire (entry_id, entry_type, status, body, extensions) |
| Relation | Directed edge between KnowledgeEntries (or KnowledgeEntry ↔ source) |
| SourceAnchor | Provenance pointer to a manuscript span or external locator |
| Finding | Checker output for consistency, style, or analysis |
| Rule | Declarative constraint input to check (L6) |
| TimelineEvent | First-class temporal object on the when-axis (L5) |
| MindState | First-class temporal mental-state record on the when-axis (L5, optional l5-mind) |
| AssemblePacket | Wire context-assembly payload (slim entries for downstream LLM prompts) |
| HostCapabilityManifest | Host roles, capabilities, and owned namespaces[] for in-process collaboration |
| Extensions | Product-specific bag on every data object (extensions.<namespace>) |
| Modules | Optional cross-product functional-dialect bag on KnowledgeEntry, AssemblePacket, and TimelineEvent (capability-flagged narrative-modules) |
| Adapter ports | Injected read/write surfaces (KnowledgeEntryPort, HostManifestPort, …) that own persistence |
| Orchestration | orchestrate* / orchestrate_* sequences that load scope, apply gates, and persist via ports |
Vocabulary and positioning: CONCEPTS.md, STRATEGY.md, and Concepts.
Products that need programmable KnowledgeEntry body state may declare l2-computable:
body.state— static durable computable valuesbody.computable— dynamic Session-scoped projectionTimelineEvent.computable_logs— Moment-scale field-change presentationproject/computeops — init/projection and apply/settle I/O envelopes
Products that need fork-scoped timeline queries may declare l5-fork. Products that exchange cross-product functional dialects (lore activation, knowledge packs, assemble placement / activation trace) may declare narrative-modules: an optional modules (ModuleMap) bag on KnowledgeEntry, AssemblePacket, and TimelineEvent carries these dialects, and adapters round-trip unknown module namespaces verbatim. Domain Profile handbooks define the inner shapes.
Products that interchange "who believes / wants / feels what at time t" may declare l5-mind: an optional MindState temporal record (snapshot / delta over the when-axis) and modules.observation on TimelineEvent.modules. The settled home of mental fields and belief labels is the holder KnowledgeEntry modules.mental / modules.belief (narrative-modules bag); MindState is strictly derivative, never a second authority. Mental-state engines stay product-owned. Opt-in, not spoke-baseline.
Products that need signed cross-process interaction may declare spoke-connect. Composed adapter aliases: BaselineAdapter, ComputableAdapter, ForkAdapter, FullAdapter.
Baseline integrators use core schemas; optional capabilities are opt-in. Detail: Concepts.
@42ch/spoke-operations / spoke-operations provide pure helpers and port-injected orchestration:
- Baseline orchestrators:
orchestrateUpsert,orchestratePromote,orchestrateRelate,orchestrateCheck,orchestrateAssemble - Optional orchestrators:
orchestrateProject,orchestrateCompute,orchestrateForkCheck,orchestrateForkAssemble - Capability-sliced ports and composed aliases (
BaselineAdapter…FullAdapter) - Extension and module map merge and round-trip preservation (
mergeExtensionMaps/mergeModuleMaps,preserveExtensionMaps/preserveModuleMaps) - Finding / KnowledgeEntry
statustransition helpers - Promote acceptance, upsert/relate, and MindState wire-shape validators (
validateMindState/validate_mind_state) - AssemblePacket builders from KnowledgeEntries
- Unified
SpokeResult/SpokeRejectCodeon reject paths
Reference FullAdapter (baseline + l2-computable + l5-fork, including HostCapabilityManifest peers): fixtures/toy-world/ — TypeScript ToyWorldAdapter, Rust crate spoke-fixture-toy-world.
Detail: Orchestrate operations.
| Topic | Guide |
|---|---|
| Protocol umbrella | Protocol |
| Nine layers and capability levels | Concepts |
| Data objects and open vocabulary | Data model |
| MindState | MindState reference |
| Ops request/response envelopes | Ops wire |
| Operations library behavior | Orchestrate operations |
| Core / modules / extensions | Data model |
| Connect envelopes and bindings | Connect |
| Domain Profiles | Domain profiles |
| JSON Schema SSOT | schemas/ |
| Reference adapters and sample graph | fixtures/toy-world/ |
Local development, CI gates, and release procedure: CONTRIBUTING.md.