Read and present state safely
Use this page to decide what each reader in your application may see and which Canwu API gives it to them. The rule is short: trusted code may read ground truth; players and in-world agents read only what their actor or institution knows.
Three terms come up throughout:
- Ground truth: facts in authoritative state, whether or not any actor knows them.
- Actor knowledge: what one person or institution (a holder) has learned, with source and time, kept in that holder’s knowledge ledger apart from ground truth.
- Projection: a detached read model built from state, such as the reference world’s
snapshot(). Changing it changes nothing in Canwu.
View diagram source
flowchart LR
State["Authoritative state"] --> Trusted["Trusted reads: entities(), boundaries(), events()"]
State --> Projection["Integration projection, e.g. snapshot()"]
Trusted --> Tools["Server, debugger, tests"]
Projection --> Renderer["Trusted renderer adapter"]
Ledger["Holder knowledge ledger"] --> Viewer["CanwuViewer: query_knowledge(), evaluation_traces()"]
Viewer --> Dto["Host-defined DTO"]
Dto --> Client["Player, agent, remote client"]
Choose by caller
Section titled “Choose by caller”| Caller | API | What it gets |
|---|---|---|
| Trusted server, debugger, or test | entities(), domain_records(), events(), boundaries(), person_availabilities(), pending_transition_manifests() |
Generic authoritative state and all recorded evidence |
| Domain rule or renderer adapter | Your integration’s snapshot() or a similar projection |
Typed domain records assembled into a detached view |
| Player or in-world agent | viewer_for_actor(), then query_knowledge() or evaluation_traces() |
Only that holder’s knowledge and the rule explanations it may see |
| Explanation UI | explain() |
A causal chain built from recorded events or a failure |
Two read paths (excerpt)
Section titled “Two read paths (excerpt)”This fragment puts a trusted projection next to an actor-knowledge query; main and error handling are omitted.
use canwu_api::{Canwu, KnowledgeQuery};use canwu_reference_world::{ReferenceWorldPlugin, demo_scenario, snapshot};
let (scenario, ids) = demo_scenario()?;let plugin = ReferenceWorldPlugin;let canwu = Canwu::new_with_plugins(35, scenario, &[&plugin])?;
let server_world = snapshot(&canwu)?;let commander_knowledge = canwu .viewer_for_actor(ids.commander)? .query_knowledge(&KnowledgeQuery::default())?;server_worldis the reference integration’s trusted projection. It contains full world truth, so keep it on the server and away from players, actor agents, and untrusted clients.commander_knowledgeholds only records in the commander’s ledger.KnowledgeQuery::default()returns current facts one page at a time; to read the next page, pass the result’snextcursor as the query’safter.
viewer_for_actor opens a viewer for any existing person in a run created without a declared RunConfiguration. In a run with a declared configuration, it works only for the actor bound to the run’s seat; call viewer() to get the viewer the run policy defines.
For your own domain, build the projection from typed_domain_record() and use CanwuViewer for actor knowledge in the same way.
Build a client-safe payload
Section titled “Build a client-safe payload”Copy the fields your UI needs from commander_knowledge into a DTO your host defines, then serialize that DTO. For example:
{ "actor": "commander", "knowledge_items": [ { "summary": "Road east is passable", "confidence": "reported" } ]}The DTO may contain only information the knowledge query returned. Keep server_world, hidden entities, and debugger-only fields out of it.
Plan from a holder’s knowledge
Section titled “Plan from a holder’s knowledge”Route planning for a person or institution should use only what that holder knows. canwu-correspondence builds a holder planning snapshot with the same rule its own plugin uses:
use canwu_correspondence::planning_snapshot_from_holder_knowledge;
let (snapshot, read_set) = planning_snapshot_from_holder_knowledge(view, &holder, observed_at)?;let plan = plan_route(&snapshot, &request)?;How to use it:
- Call it inside a plugin boundary system or command handler whose contract declares the
canwu.core.knowledgeread (StateKey::core_knowledge()).viewis that system’sSimulationView. - Take
holderfrom admitted authority, such as the command issuer. The view has system-level access, so a holder taken from an unvalidated payload could read someone else’s knowledge. - The snapshot includes exactly the endpoints and connections the holder’s ledger asserts at the view’s read cut, the point in the ledger’s history that the view reads. Its
knowledge_cutfield records that cut. - The call fails if a known connection names an endpoint the holder does not know.
read_setis aKnowledgeReadCutDigest: evidence of which facts the planner read. It stays the same while those facts are unchanged.
Outside the runtime, pass a query result you already hold to the pure function planning_snapshot_from_knowledge_result, for example a restricted viewer’s query_knowledge(&planning_knowledge_query()). It rejects a paginated result, because a partial page cannot show the holder’s whole network.
Explain a rule result
Section titled “Explain a rule result”When a boundary system records an evaluation trace, a player or agent can see how a rule reached a number without seeing the evidence behind it:
let viewer = canwu.viewer_for_actor(ids.commander)?;for trace in viewer.evaluation_traces(&EntityRef::Army(ids.army), None)? { println!("{} {} = {}", trace.rule_id, trace.rule_version, trace.result); for term in &trace.terms { println!(" {}: {}", term.term_id, term.contribution); }}evaluation_traces(subject, after) returns EvaluationTraceView values for boundaries after after, or for every retained boundary when after is None, in boundary and recording order. Each view has the rule, subject, boundary, result, and each term’s contribution. Evidence and the producing plugin and system stay in the trusted record.
Who sees which traces:
| Principal | Traces returned |
|---|---|
| Person or institution | Traces about its own entity. Traces about another subject only if a knowledge publication naming that subject reached its ledger before the trace was evaluated, so a holder sees breakdowns only for subjects it already knew about. |
| Research or developer | Every trace |
| Public | None; the call returns InvalidKnowledgeAuthority |
Trusted code reads the full trace records, with evidence, through boundaries().
Follow multi-owner transitions
Section titled “Follow multi-owner transitions”A transition manifest lets several plugins write one transition together at a named boundary (see transition manifests). Reading them:
- Host (trusted):
pending_transition_manifests()lists registered manifests whose ready boundary has not settled yet.BoundaryReceipt::transition_auditsandBoundaryRecord::transition_auditslist the manifests that settled at that boundary, eachCommittedorExpired. - Inside the simulation: only a manifest’s coordinator and participants see it. Their boundary systems declare the
canwu.core.transitionsread and callSimulationView::transition_manifestsandSimulationView::transition_audits. - Players: show a transition through your application’s own records or reports. The audit is trusted evidence.
Keep presentation state in the renderer
Section titled “Keep presentation state in the renderer”A renderer may copy locations, routes, and statuses from a projection and keep its own animation and interpolation data. That data stays in the renderer; Canwu state changes only through commands and ingress. Continuous clients keep three clocks apart: wall time, simulation time, and presentation time. The continuous-time game loop tutorial shows how.