Skip to content

Knowledge and information

Canwu keeps what is true in the world apart from what each actor knows. canwu-knowledge holds one knowledge ledger per holder, the person or institution whose knowledge it records; canwu-information records how a piece of information is written, sent, read, understood, and released; and canwu-correspondence carries addressed letters along routes planned from what the carrier knows. Use them when players, agents, or institutions should act on reports and letters as they arrive, each seeing only what has reached it.

Crate What it owns Published
canwu-knowledge Holder ledgers (record bodies stored once, a holder entry per holder, assembled as KnowledgeRecord), holder queries, read cuts (the ledger position a read saw), cursors, and the retention policy, plus each actor’s legacy army observations, kept for compatibility (ActorKnowledge) Published on crates.io
canwu-information The information lifecycle as domain records in the canwu.information namespace: channels, content, representations, instances, dispatches, delivery attempts, access, interpretation, audiences, releases, and operations; the simulation plugin InformationPlugin Published on crates.io
canwu-correspondence Addressed correspondence in the canwu.correspondence namespace: operations, communication opportunities, knowledge seeds, and carrier delegations; the simulation plugin CorrespondencePlugin; the holder planning snapshot builders Published on crates.io
Crate dependencies: the host application uses the two extensions and the public API; the extensions depend on canwu-api; the simulation core stores canwu-knowledge ledgers. View diagram source.
View diagram source
flowchart TB
  Host["Host application<br/>scenario, commands, ingress, seeds"]
  Corr["canwu-correspondence<br/>letters, routes, carriers"]
  Info["canwu-information<br/>information lifecycle"]
  Api["canwu-api<br/>public API, CanwuViewer"]
  Route["canwu-routing, canwu-transport<br/>route planning, transport records"]
  Sim["canwu-sim<br/>validates and commits publications"]
  Know["canwu-knowledge<br/>holder ledgers and queries"]
  Host --> Corr
  Host --> Info
  Host --> Api
  Corr --> Info
  Corr --> Api
  Info --> Api
  Api --> Route
  Api --> Sim
  Api --> Know
  Sim --> Know

Arrows point from a crate to what it depends on. canwu-knowledge is a model crate: the simulation core (canwu-sim) stores its ledgers and validates every publication, and canwu-api re-exports its types. The two extensions depend only on canwu-api, and canwu-correspondence also depends on canwu-information. The host application registers the plugins, supplies content and probabilities, and reads results through the public API.

Publishing knowledge is a separate capability from owning state. A plugin that owns a record still needs a registered knowledge schema and a KnowledgeWriteGrant on one of its boundary systems (the per-phase functions a plugin registers) before it can tell any holder about that record.

Type What it represents
KnowledgeHolderRef Who knows: Person(PersonId) or Entity(EntityRef). Defined in canwu-core and re-exported by canwu-api.
KnowledgeRecord One fact in one holder’s ledger, as a reader gets it: its schema, typed subjects, a JSON payload, as_of (when the fact held, if known), learned_at (when the holder learned it), confidence_per_mille, origin (method and evidence), and the earlier records of the same holder it supersedes or contradicts. The gap between as_of and learned_at is the age of the news.
KnowledgeRecordBody, HolderEntry How the ledger stores a record: the record body (everything but the holder and the relations), kept once however many holders hold it, and one holder entry per holder with the holder’s own local ID and its own supersedes and contradicts. A KnowledgeRecord is a body assembled with one entry; a body leaves the ledger when no holder holds it.
KnowledgeRecordDraft What a boundary system proposes. The kernel assigns the record ID and sets learned_at to the boundary time. payload_bytes() is the number of bytes the publication budget charges for it.
KnowledgeRecordView The holder-facing copy of a record: it carries a holder-local ID (HolderKnowledgeRecordId, from the holder’s own counter and stored with the entry) and omits origin.
KnowledgeQuery, KnowledgeQueryResult, KnowledgeCursor One page of one holder’s ledger, filtered by schema, subject, and learned time, with a cursor for the next page and the retention of each schema the query is about.
KnowledgeReadCut The ledger position a read saw: the boundary and a hash of that holder’s records.
KnowledgeSnapshot Every holder ledger, as stored in a snapshot or a scenario: the bodies in ID order and, per holder, its next local ID and its entries. It also carries ActorKnowledge: each actor’s legacy observations of each army, kept for compatibility. Each entry has a KnowledgeSource (direct observation, command responsibility, report, or scenario record).
PluginKnowledgeSchema, KnowledgeWriteGrant A versioned knowledge schema that one plugin registers and owns, with its retention policy (KnowledgeRetention::Keep or RetireWhenSuperseded), and one boundary system’s permission to publish that schema with a given visibility.
KnowledgePublicationGroup, KnowledgeSupersession One group of a PublishKnowledge directive: the holders (sorted, unique) that every draft of the group goes to; and how the entries name what they supersede: Explicit (the drafts’ own lists, for a group of one holder) or CurrentOnSubjects (the engine names each holder’s current records of the draft’s kind and subjects).
KnowledgePublicationBudget What the boundary may still publish at a system’s cut: record bodies, holder entries, and payload bytes, with the per-directive bounds. Read with SimulationView::knowledge_publication_budget().

Each information record type below has a matching payload type, such as ContentPayload for Content.

Type Crate What it represents
Content information What is said: an inline JSON body or the digest of an external resource, optionally derived from other content, with each source’s role, such as quotation or correction
Representation information One encoding of content in a format, with its lineage, an optional ClaimedSourceRef (the source it claims), and the capability needed to interpret it
Instance information A physical copy, with its custodian and location
Channel information A path profile and its capabilities, such as AddressedDelivery, AudienceDelivery, or OpenReception
Dispatch, DeliveryAttempt information One send to addressed holders, an audience, or open reception; one try to reach one recipient
Access information A holder read a representation, with a method and an extent in thousandths
Interpretation, AuthenticityFinding information A holder’s attempt to understand what it accessed, with status Failed, Partial, or Succeeded; the optional finding records whether it accepts the claimed source
Audience, Release information A frozen member list bound to a membership root; a release to that audience or to open availability, which ends as Withdrawn or Expired
InformationOperationEnvelope information One idempotent operation that wraps one LifecycleRequest under an InformationOperationId
CorrespondenceOperation correspondence One addressed letter: its intent, the resolved address and read cut, the RoutePlan, the TransportExecution, planning history, incidents, and a CorrespondenceStatus that ends as Settled, DeadlineMissed, CompensationPending, or Failed
NetworkKnowledgeSeed correspondence Routing endpoints, connections, and recipient addresses to install in one holder’s ledger
CommunicationOpportunity correspondence A drawn chance for a sender to write to one of its candidate recipients
CorrespondenceIncidentKind correspondence Disaster, Interception, or CarrierSeized
CarrierAuthority correspondence A carrier’s accepted delegation to carry for a sender, kept as its current CarrierDelegationRecord
Path from a command or ingress through phase-7 lifecycle systems and phase-13 publication to the holder ledger, the KnowledgePublished event, and CanwuViewer. View diagram source.
View diagram source
flowchart TB
  Input["Host commands and ingress<br/>apply_information_operation_v1,<br/>initiate_correspondence_v1, seeds"]
  Corr["Phase 7: correspondence-lifecycle-v1<br/>reads carrier ledger, plans route"]
  Info7["Phase 7: information_lifecycle_v1<br/>validates and writes records"]
  Info13["Phase 13: information_publication_v1<br/>PublishKnowledge, one group per holder"]
  Seed["Phase 13: correspondence-knowledge-ingress-v1<br/>seeds and attempt reports"]
  Kernel["Simulation core<br/>checks schema, grant, holder, budget<br/>commits at boundary end"]
  Ledger["Holder knowledge ledger<br/>one body, one entry per holder"]
  Event["KnowledgePublished event<br/>audience: the holders named"]
  Viewer["CanwuViewer<br/>query_knowledge, visible_changes_since"]
  Input --> Corr
  Input --> Info7
  Input --> Seed
  Corr -- "information_operation_v1<br/>admitted at a later boundary" --> Info7
  Info7 --> Info13
  Info13 --> Kernel
  Seed --> Kernel
  Kernel --> Ledger
  Kernel --> Event
  Ledger --> Viewer
  Event --> Viewer
  Ledger -. "next planning read" .-> Corr
  1. Input arrives. Your host submits apply_information_operation_v1 commands, each carrying an InformationOperationEnvelope. The command handler checks idempotency and queues information_operation_v1 canonical ingress; the command itself writes no records. An information provider may queue that ingress directly. Correspondence enters through the initiate_correspondence_v1, delegate_carrier_v1, and resolve_correspondence_v1 commands and the install_correspondence_knowledge_v1, communication_opportunity_v1, and correspondence_incident_v1 ingress.

  2. Phase 7 runs the information lifecycle. The event-driven system information_lifecycle_v1 moves each admitted operation through Accepted and ApplyingDomainChanges and then applies it, one state per boundary. Before applying, it checks the interpretation authority and that any authenticity finding cites the current version of a representation that carries a claimed source. A failed check stores the operation as Rejected with the code invalid_authority or invalid_lifecycle. InformationLifecycle::plan returns the record mutations and the knowledge publications the change implies. Operations are planned oldest first; one whose plan touches a record an earlier operation of the same pass already changes waits for the next boundary. A packet that does not decode, or that carries different input for an operation ID another packet of the boundary or the persisted operation already has, is skipped with a canwu.information.ingress_rejected.v1 event and never fails the boundary. The same system keeps, per command grant, one versioned DelegatedAuthority record, the delegation ledger: the grant’s live delegations by command ID with an expiry index. A later InterpretationAuthority::Delegated is checked against that ledger rather than the command, so it decides alike after the command is sealed. At every boundary it runs, the system removes the delegations whose expires_at has come and does not record a claim that was already expired when its command was admitted; a delegation whose expires_at has come authorizes nothing even before it is removed, so an interpretation must be admitted while its delegation is in force, even if it is dated inside the delegation window (invalid_authority otherwise). A claim with no expires_at stays for the run.

  3. Phase 7 also runs correspondence. correspondence-lifecycle-v1 settles starts, progress, incidents, opportunities, recoveries, and delegations. A start reads the carrier’s current planning knowledge, resolves the recipient’s address from it, calls plan_route, stores the CorrespondenceOperation, and schedules information operations for canwu-information. Later progress_correspondence_v1 ingress waits for those operations and starts and completes legs.

  4. Phase 13 publishes. information_publication_v1 turns an operation’s publications into PublishKnowledge directives, one group of one holder each, at most 64 per operation in a boundary and never past the boundary’s publication budget (a chunk whose first record does not fit is deferred whole, and the next boundary at the same instant retries); each holder, kind, and subject set is published at most once per boundary, and a holder the engine does not accept (a dead person, a missing army or government, a retired entity) is left unpublished while the operation completes with the rest. The internal information_operation_finalize_v1 ingress records the published IDs at the next boundary, found through knowledge_heads rather than retained evidence, and releases the next chunk. correspondence-knowledge-ingress-v1 publishes knowledge seeds and carrier attempt reports: a seed whose holder the engine refuses, that one publication cannot carry, or that states a connection no planning network admits publishes nothing and its receipt records why (KnowledgeSeedRefusal, a seed refusal); an attempt report to a carrier the engine refuses (a person who died after the seizure, say) is withheld with canwu.correspondence.attempt_report_withheld.v1, and the boundary stands.

  5. The simulation core checks and commits. It accepts PublishKnowledge only when:

    • it comes from a phase-4 or phase-13 system that lists the schema in knowledge_writes with that visibility;
    • the schema is writable and registered by the same plugin;
    • every holder of every group is eligible and the evidence references are valid;
    • every record an entry supersedes or contradicts is a visible record of the same holder and schema kind (see superseding an earlier record), or, under CurrentOnSubjects, the drafts carry no relations and the engine computes them per holder;
    • the directive stays within its per-directive bounds and the proposal within what the budget left for this system.

    It then assigns one global record ID per body, sets learned_at, lets later systems in the same boundary read SameBoundary records, and at the end of the boundary stores each body once and gives each holder of its group an entry with the holder’s next local ID; under RetireWhenSuperseded the entries those entries supersede leave their holders in the same commit (see retention).

  6. Results come out. Each directive becomes a BoundaryKnowledgeChange in BoundaryRecord::knowledge_changes (its bodies, shared with the ledger rather than copied, and each holder’s entries; records_of(&holder) assembles what one holder received) and one KnowledgePublished event naming every holder it published to, each of which is in its audience. The holder can read the new records from the next read of its ledger.

A group with several holders publishes the same bodies to all of them: one copy of each body in the ledger, the journal, and every hash, and one entry per holder. Each holder sees its own local IDs and its own relations, and nothing tells it that another holder holds the same body. Supersession is per holder: with KnowledgeSupersession::CurrentOnSubjects, the engine runs the knowledge_heads rule for each holder of the group and names, in that holder’s entry, its current records of the draft’s kind that carry every subject of the draft, so a shared line supersedes each holder’s own earlier record. A draft whose match for some holder has more than 64 current records fails the boundary: a shared line is meant to have one current record. Explicit keeps the drafts’ own lists and is allowed only for a group of one holder; a group of one holder with Explicit is exactly the directive of 0.16.0, which is how every first-party publisher ports.

A boundary may store at most 32,768 record bodies, add 32,768 holder entries, and take 64 MiB of payload (KnowledgeLimitsV1::CURRENT), and one directive at most 1,000 bodies and 4,096 entries. Every publishing system of the boundary shares that budget, in dispatch order: phase 4 before phase 13, and within a phase in the order the registry runs the systems. Before it publishes, a system reads what is left:

let mut budget = view.knowledge_publication_budget();
let bytes = draft.payload_bytes()?;
if budget.take(1, holders.len(), bytes) {
// publish this draft to these holders
} else {
// defer it, or record a rejection, as the plugin's own state can see
}
  • KnowledgePublicationBudget holds the remaining records (bodies), holder_entries, and payload_bytes, and the per-directive bounds records_per_batch and holder_entries_per_batch. admits says whether one more directive of the given size fits; take subtracts it when it does and says whether it did.
  • The read needs no declaration. Its answer is exact: the limits less what every proposal of earlier phases and of earlier systems of this phase took, so a live run, a sealed run, a restore, and an exact replay see the same remainder.
  • KnowledgeRecordDraft::payload_bytes() is exactly what the engine charges for the draft’s payload (its compact JSON length), so a publisher never has to guess.
  • A publisher that reads the budget and defers what does not fit never fails a boundary. One that publishes past it fails the boundary with KnowledgeLimitExceeded: the engine does not cut a publication to fit, because the plugin’s own record writes in the same proposal would then believe the records were published. The first-party publishers (military, movement, information, technology, and the fiscal report system) all read it first.
  • There is no share per system by default. If two plugins compete for the budget, the earlier one in dispatch order takes what it needs; which plugin comes first depends on how you compose them.
  • A publication reservation gives one plugin a guaranteed share. Register the plugin through KnowledgeReservedPlugin::new(&plugin, KnowledgePublicationReservation { records, holder_entries, payload_bytes }), or let the plugin call registrar.reserve_knowledge_publication(reservation) in its own register. A boundary holds the reservations of every plugin whose knowledge-writing system (phase 4 or 13 with a knowledge write grant) is due in it, decided before the first phase from the boundary’s cadences and admitted events; the systems of other plugins are not shown, and cannot take, that unspent share whatever their dispatch order, and the reserving plugin reads its share through the same knowledge_publication_budget(). A plugin publishing in phases 4 and 13 has one share for both, held through the whole boundary. Registration refuses an empty reservation, a second one, one for a plugin with no knowledge-writing boundary system, and reservations that together exceed the per-boundary limits; the reservation is part of the plugin descriptor, so a run restores and replays only with a plugin that declares the same one. Without reservations the figures are those of the plain budget. Overshooting what a system was shown still fails the boundary.

Each registered schema declares a KnowledgeRetention:

  • Keep (the default): every entry stays in its holder’s ledger for the life of the run. Superseded records remain readable with KnowledgeHistoryView::FullHistory.
  • RetireWhenSuperseded: when a new entry of a holder supersedes one of the holder’s entries, the superseded entry is cleared from that holder in the commit of the publishing boundary. Every relation of the holder that named it is dropped, so every relation still resolves; when no holder holds its body any more, the body leaves the ledger. The holder keeps one entry per line, and held knowledge stops growing with history.

Retention is a state transition of the boundary, the same in the live run and in its exact replay, and never tied to a seal, so a run that seals and one that does not reach the same state hash. The policy is part of the plugin descriptor, so a run restores and replays only with a plugin that declares the same policy. What a reader sees: CurrentHeads queries are unchanged; FullHistory of a retiring schema shows only current entries, and KnowledgeQueryResult::retention says which policy each schema has; knowledge_changes_by_correlation still returns what a boundary published, from retained evidence, whether or not an entry was cleared since; a snapshot holds only current entries; a holder emptied by retention keeps its local-ID counter, so local IDs never restart. An entry of a dead person is never cleared, because nothing can be published to supersede it.

The settlement system page lists all fourteen phases. The routed correspondence case walks through incidents and recovery: a disaster reroutes the same attempt, an interception gives the interceptor an Access record while delivery continues, and a carrier seizure ends the attempt.

When a plugin publishes a newer version of something a holder already knows, such as a fresh report, the new record names the old one in supersedes. The holder’s default query then shows only the new one.

A record may supersede or contradict only a record of the same holder and schema kind that is visible at the publisher’s cut. That means the target was committed by an earlier boundary, whatever its learned time, or was published by the same boundary with SameBoundary visibility in an earlier phase. Two boundaries can settle at one instant, and the second may supersede what the first published. A load applies the same rule as settlement, so a save loads exactly when the run that wrote it could continue.

To find the records to supersede, a boundary system that declares the StateKey::core_knowledge() read calls SimulationView::knowledge_heads:

let heads = view.knowledge_heads(&holder, &schema.kind, &[subject])?;
draft.supersedes = heads.records;
  • It returns the global IDs of the holder’s current records of that schema kind, that is, the entries no entry of the holder names in supersedes. A record matches when it carries every listed subject, and an empty list selects the whole kind. If your plugin keeps several lines of records for one holder, give each line a subject the others lack. A directive with KnowledgeSupersession::CurrentOnSubjects makes the engine do this read for every holder of a group, with the draft’s own subjects.
  • Every returned ID is a valid target for a record the system publishes to the same holder with the same kind, and the IDs are already in the ascending order a draft requires.
  • At most 64 IDs come back, the number one record may supersede. When more records match, the newest are returned and truncated is set. That is not an error: the older current records are superseded by later publications.
  • The answer comes from committed state and the boundary being settled. A live run, a run that sealed its evidence, a restored run, and an exact replay therefore return the same IDs.
  • The read costs the current records of the kind that carry the narrowest listed subject, not the holder’s whole ledger: each holder ledger keeps an index of its current records by kind and subject, derived from the records and never saved. A boundary that publishes nothing does no work in proportion to the knowledge a run holds, and a publishing boundary pays for its own records only.

A ledger belongs to one holder: a person who is not dead (KnowledgeHolderRef::Person), an army, a government, or a domain-record entity whose schema sets holder_policy to KnowledgeHolderPolicy::Allowed. Publishing to any other holder fails with InvalidKnowledgeHolder. Ledgers have no inheritance: a successor office starts with an empty ledger until a plugin publishes to it.

Reader API What it gets
Player or agent bound to a person or institution CanwuViewer::query_knowledge Its own ledger as KnowledgeRecordView values, with no origin evidence
Research or developer principal CanwuViewer::query_holder_knowledge, CanwuViewer::audit_knowledge_record Any existing holder’s ledger; the audit call returns one full KnowledgeRecord with its origin
Public principal none query_knowledge fails with InvalidKnowledgeAuthority
Trusted host code Canwu::admin_query_knowledge, Canwu::knowledge, and, for current records only, knowledge_heads and knowledge_current_records Any holder’s ledger, or the whole KnowledgeSnapshot
Boundary system or command handler SimulationView::knowledge_records Any holder’s ledger at the view’s read cut; the contract must declare the StateKey::core_knowledge() read

Open a viewer with Canwu::viewer(), which follows the run’s observation policy, or with viewer_for_actor for a character seat. The reading state page shows both and explains how to build a client payload.

A query returns records in learned_at order. KnowledgeHistoryView::CurrentHeads, the default, hides records that a later record supersedes; FullHistory returns all of them that the schema’s retention kept, and the result’s retention map says which policy applies. Holder-local IDs come from the holder’s own counter and are stored with the entry, so they never change, never restart, and tell a reader nothing about how many facts other holders have. A cursor binds the holder, the query, and the read cut. When the holder learns something new, its read cut changes and an old cursor fails with KnowledgeReadCutUnavailable; another holder’s news leaves the cursor valid.

Since 0.22.1, a page costs the page it returns, not the holder’s history. Each holder ledger keeps its entries, and separately its current entries, in order of schema, learned_at, and local ID. A page jumps to its learned_after time or its cursor in each schema it names, reads forward, and stops one match past its limit or past learned_at_or_before. It reads the records it returns plus those its subject filter skips, so a page that filters only by subject may still read most entries of its schemas. The read cut comes from a hash the ledger keeps up to date as entries arrive, so working it out reads no entry, and its value is what 0.22.0 computed. The cost moves to the commit. When an entry leaves a holder, through retention or removal, the commit re-hashes that holder’s later entries once, so clearing an old entry under a long history costs that history once. Each new entry costs a few microseconds more, loading a save re-hashes every entry, and a ledger uses about a tenth more memory per entry.

A trusted host that needs only one holder’s current records of a schema kind does not have to page the ledger. knowledge_heads(holder, kind, subjects) on Canwu, CompactedCanwu, and any CanwuRun returns, for the same committed state, the global IDs that SimulationView::knowledge_heads returns in a boundary (at most 64, ascending, the newest when more match; naming more than 64 subjects fails with KnowledgeLimitExceeded). knowledge_current_records(holder, kind, subjects) iterates the same records, newest first in publication order, as the holder-relative KnowledgeRecordView values a CurrentHeads page returns. Both read the holder’s index of current records and compute no read cut, so they cost about the records they read; a caller that needs a cursor bound to a read cut pages with admin_query_knowledge, which costs its page. They return any holder’s payloads, so they are host-only reads: CanwuViewer has no such call, and the host selects what belongs to an actor before anything reaches one.

CanwuViewer::visible_changes_since(since) returns a VisibleChange (time, summary, and source event) for each later event whose audience includes the viewer. Every knowledge publication emits one KnowledgePublished event naming every holder it published to (holders, with the directive’s record_count and entry_count), and each of those holders is in its audience, so a client can use this call to notice new knowledge and then query the ledger. Research and developer principals see every event. The event system page describes the other audiences.

The two extensions publish these schemas:

Schema Published to When
canwu.information representation_available The recipient of a delivery attempt The attempt becomes Delivered
canwu.information access_recorded The holder who read An access is recorded
canwu.information interpretation_recorded The holder the interpretation was performed for An interpretation is recorded
canwu.information release_available Each audience member An audience release becomes Active
canwu.correspondence routing_endpoint, routing_connection, address The holder named in the seed A NetworkKnowledgeSeed is admitted
canwu.correspondence attempt_report The carrier A carrier seizure ends a delivery attempt, unless the carrier accepts no knowledge by then

The information schemas carry only record_version in the payload; the subjects name the records. Your host decides which content to show once a holder knows a record exists. Each fact goes to one holder, so a relay’s secret read reaches no one else, a delegated carrier’s route knowledge stays out of the sender’s ledger, and a seizure report names no seizer.

canwu-knowledge and canwu-information draw no random numbers. canwu-correspondence declares one random stream, operation-resolution version 1, on correspondence-lifecycle-v1. It makes these operation-keyed draws:

Operation kind Decides
communication_opportunity Whether an opportunity is selected: a draw from 0 to 999 below your probability_per_mille selects it
communication_recipient Which candidate a selected opportunity names: a uniform draw over the candidate list
correspondence_incident Whether an incident triggers: a draw from 0 to 999 below your probability_per_mille triggers it

The 0–999 roll is stored on the opportunity or incident record as roll_per_mille. Whether a holder detects a forged claimed source is your application’s own draw; the information extension records only the resulting AuthenticityFinding.

Holder ledgers are saved in SimulationSnapshot::knowledge as the bodies in ID order and, per holder, its next local ID and its entries in local order. Loading rejects a body no holder holds, an entry that names a missing body, a local ID out of order or at or above the holder’s counter, a relation that does not resolve within its holder, and a duplicated body or holder. Holder-local IDs are stored; read-cut hashes and cursor bindings are derived from the entries and never stored, and the page order and the read cut’s hash are rebuilt when a save loads. The knowledge commitment is chunked: a leaf hash per entry, chunk roots over 256 local IDs, a root per holder, and one ledger root, so a publication rehashes only the chunks it touched and an entry can leave. Information and correspondence state are ordinary domain records.

Each boundary record keeps its knowledge_changes, and exact replay recomputes and compares them; a restore rebuilds the ledger boundary by boundary from the journal, applying the supersession rule, the budget, and retention, and refuses a save whose ledger does not match. Information operations are idempotent by ID and canonical input hash: an exact retry changes nothing, and different input under the same ID fails with IdempotencyConflict. Decoding and other judgments happen in your code, and their results enter as recorded content and interpretation records, so replay reuses them without running your decoder again.

  • Holders and their starting knowledge, through Scenario::knowledge or, for correspondence planning, a NetworkKnowledgeSeed.
  • Your own knowledge schemas and the phase-4 or phase-13 systems that decide who learns what from your mechanics.
  • Information content: bodies, formats, claimed sources, channel profiles, and audience member lists.
  • Interpretation results, including any AuthenticityFinding.
  • Authority: controllers and decision tickets for sending and recovery (correspondence_decision_ticket and correspondence_recovery_decision_ticket build them), carrier delegation commands, and, for delegated interpretation, a plugin named canwu-authority that accepts delegate_interpretation_v1 commands.
  • Probabilities for opportunities, incidents, and forgery detection.
  • The client DTOs built from query_knowledge and visible_changes_since results.

The knowledge ledger (DEFAULT_KNOWLEDGE_PAGE_SIZE, MAX_KNOWLEDGE_PAGE_SIZE, and KnowledgeLimitsV1::CURRENT):

Limit Value
Default and maximum query page size 100 and 1,000 records
Knowledge schemas per plugin (schemas_per_plugin) 256
Record bodies per directive, over all its groups (records_per_batch) 1,000
Holder entries per directive: holders × drafts, summed over its groups (holder_entries_per_batch) 4,096
Record bodies per boundary, over all publishers (records_per_boundary) 32,768
Holder entries per boundary (holder_entries_per_boundary) 32,768
Payload bytes of new bodies per boundary (payload_bytes_per_boundary) 64 MiB
Payload bytes per record (payload_bytes_per_record) 65,536
Subjects, evidence references, supersedes links, and contradicts links per record (relations_per_record) 64 each
Summary and origin-method text (text_bytes) 1,024 bytes

The per-boundary rows are one budget shared by every publisher, read with knowledge_publication_budget(); there is no per-system count, only the share a publication reservation holds for a plugin. InformationLimitsV1::canonical() caps addressed recipients per dispatch and explicit audience members at 10,000 each, delivery attempts per recipient at 256, and an inline body at 65,536 bytes; the rustdoc lists the rest.

A communication opportunity lists 1 to 64 candidate recipients.

终端窗口
cargo run -p canwu-information --example confidential_copy_release
cargo run -p canwu-information --example encoded_interception
cargo run -p canwu-correspondence --example routed_correspondence
cargo test -p canwu-knowledge -p canwu-information -p canwu-correspondence