Skip to content

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.
Two read paths: trusted code reads authoritative state and projections; players and agents read one holder's knowledge through CanwuViewer, and the host copies selected fields into a client payload. View diagram source.
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"]
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

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_world is 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_knowledge holds 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’s next cursor as the query’s after.

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.

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.

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:

  1. Call it inside a plugin boundary system or command handler whose contract declares the canwu.core.knowledge read (StateKey::core_knowledge()). view is that system’s SimulationView.
  2. Take holder from 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.
  3. 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_cut field records that cut.
  4. The call fails if a known connection names an endpoint the holder does not know.
  5. read_set is a KnowledgeReadCutDigest: 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.

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().

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_audits and BoundaryRecord::transition_audits list the manifests that settled at that boundary, each Committed or Expired.
  • Inside the simulation: only a manifest’s coordinator and participants see it. Their boundary systems declare the canwu.core.transitions read and call SimulationView::transition_manifests and SimulationView::transition_audits.
  • Players: show a transition through your application’s own records or reports. The audit is trusted evidence.

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.