Production, resources, and transport: who owns what
Use this page when your game or model has stock that can run out, such as grain, cloth, ammunition, or building materials. It shows which crate owns each step, the order the steps happen in, and the rules for demand sources, access grants, loss, exchange, local handover, production output, and custom consumers.
The optional canwu-resource and canwu-production extensions handle this. The simulation core stays generic: grain, factories, prices, historical deposits, and armies live in extensions, content, and your application.
Who owns what
Section titled “Who owns what”| Owner | Owns |
|---|---|
| Simulation core | Command admission, deterministic time, phase order, atomic commit, rollback, visibility, persistence, and replay |
canwu-resource |
Resource and unit revisions, accounts (ResourceAccount), protected floors, demands (ResourceDemand), reservations (ResourceReservation), allocation legs, transfers, consumption, loss, and fulfillment |
canwu-production |
Processes, production sites, facilities, capacity, work orders, work in progress, execution, projects, and output settlement. It uses resource outcomes and technology evidence and keeps no inventory of its own. |
canwu-transport and canwu-movement |
Itineraries, legs, custody handoffs, and delivery completion; canwu-movement runs movement executions and capacity pools. Cargo balance, loss, acceptance, return, and destination credit stay in canwu-resource. |
| Your population, military, and other domains | Forming demand and reacting to fulfillment results |
| Your application | Money, markets, wages, building UI, combat formulas, and historical content |
canwu-resource is the only writer of stock balances. Other domains submit resource operations and read the outcomes; they must not write balances or another domain’s records. All of these crates sit on top of the public canwu-api.
Key terms
Section titled “Key terms”| Term | Meaning |
|---|---|
| Account | One stock balance (balance) of one exact resource and unit revision, held by a custodian: the holder responsible for that stock. |
| Protected floor | An amount an account holds back from ordinary allocation, such as seed grain (ProtectedFloorPolicyRevision). A demand whose protection_override_class the policy lists may draw below it. |
| Demand | A requester’s request for a quantity, with a minimum useful amount, a partial-fulfillment policy, due and expiry times, a priority, and a source policy. |
| Reservation and allocation leg | The result of allocation: a quantity set aside for one demand in one account. A released or expired reservation frees the quantity again. |
| Transfer escrow | Stock that has left its source account and is in transit. It stays counted until it is accepted, lost, or returned. |
| Completion lease | A holder’s right to perform one irreversible operation, named by its operation key. Activating the lease yields a CompletionLeaseActivationCertificateV1, which pins the operation key, the time, and the exact records the operation depends on. Irreversible requests carry this certificate. |
| Adapter ingress | The path provider plugins use (enqueue_resource_adapter_operation). Each operation cites the exact provider record version that justifies it. |
A tracked command is a command sent with a CommandRequest (request ID and expected revision). Build the resource command with resource_command(&ResourceCommandV1 { subject, request }); the issuer must control subject.
Lifecycle
Section titled “Lifecycle”View diagram source
flowchart TB
Demand["Demand: requester sends SubmitDemand command"] --> Alloc["Allocation: host queues enqueue_resource_allocation, canwu-resource reserves stock"]
Alloc --> Consume["Consume: consumer plugin via adapter ingress"]
Alloc --> Begin["BeginTransfer: source custodian or grantee command, stock moves to escrow"]
Begin --> Move["Transport: canwu-transport and canwu-movement"]
Move --> Accept["Accept via adapter ingress"]
Begin -. "same place_scope" .-> Local["AcceptLocal: destination custodian"]
Move --> LoseReturn["Lose or Return: transfer controller"]
Accept --> Credit["Destination account credited"]
Local --> Credit
Credit --> Later["Later boundary: consumers use the stock"]
- Demand. The requester submits
SubmitDemand(and laterAmendDemandorCancelDemand) as a tracked resource command. Adapter ingress cannot submit or amend demands. - Allocation. The host queues an allocation pass for one requester with
enqueue_resource_allocation.canwu-resourceallocates deterministically, using protected floors, minimum useful amounts, partial-fulfillment policies, due times, priorities, and stable tie-break keys, and records reservations and allocation legs. - Debit. A consumer plugin consumes an allocation through adapter ingress (
Consume), or the source custodian (the grantee, for a granted allocation) starts a transfer (BeginTransfer) that moves the quantity into escrow. - Transport.
canwu-transportandcanwu-movementcarry the cargo. Transfer progress (AdvanceTransfer) arrives through adapter ingress and cites transport evidence. - Arrival.
CompleteTransfersettles the transfer:Acceptthrough adapter ingress credits the destination;AcceptLocalhandles a same-place handover;LoseandReturnend it otherwise. - Use. Production, force supply, and other consumers use the credited stock in a later boundary. They cite exact allocation and fulfillment evidence and write only their own records.
Every transfer step names the transfer’s expected_transfer_revision, so a stale or repeated step is rejected and one arrival cannot be credited twice. If a conservation, revision, or authority check fails, the operation’s changes roll back together.
canwu-resource checks conservation with ResourceState::validate_conservation over ConservationTotalsV1:
closing balances + closing transfer escrow = opening balances + opening escrow + admitted production + external inflow - admitted consumption - admitted loss - external outflowOnly balance is stored. Available, reserved, and protected amounts are derived (ResourceState::account_quantities). A route estimate, an accepted arrival, and a consumption are three separate facts, and each needs its own step.
For a runnable fourteen-month grain loop, run cargo run -p canwu-economy-reference --example grain_loop and read the production, resources, and military supply case.
Demand source policy
Section titled “Demand source policy”Every ResourceDemand stores a source_policy (ResourceDemandSourcePolicyV1) that picks which accounts may supply it:
| Policy | Eligible accounts |
|---|---|
Pooled (default) |
Every open account with the matching resource and unit revisions, visited in account-ID order |
ExactAccounts(accounts) |
The listed accounts only. Each must exist, be open, match both revisions, and have the requester as custodian. |
Granted { grant_id, accounts } |
The listed accounts only. Each must be custodied by the grantor of the named access grant, and the requester must be its grantee (see delegated access grants). |
Rules:
ExactAccountsandGrantedlists hold 1 to 256 accounts (MAX_DEMAND_SOURCE_ACCOUNTS), in strictly increasing ID order with no duplicates. An invalid list rejects the command before any stock changes, and allocation checks the list again.- Allocation computes available supply, minimum useful quantity, and partial fulfillment from the selected accounts alone. If the listed accounts cannot cover the demand, the demand gets only what they hold. Protected floors and later transfer and consumption checks still apply.
- You can change the policy with the expected demand revision until the demand’s first allocation. After it has any reservation (including a consumed one that backs an in-flight transfer) or any fulfillment, the policy is fixed; cancel the demand and submit a new one. Cancelling a demand leaves transfers it already started in place.
AmendDemandcannot change a demand’s status, rejection reason, or requester; such an amendment is recorded as a rejection. Terminal demands cannot be amended.
Authority: Pooled and ExactAccounts choose where supply comes from; they give no right to spend another custodian’s stock. Your application controls who may submit pooled demands and under which requester. To let one custodian draw on another’s stock, with a cap and a time window, use an access grant and a Granted policy. Appointments, purposes, and who may grant to whom stay with your application.
Persistence: the policy is part of request digests, runtime snapshots, exact replay, and terminal demand archives. Holder-relative reports do not reveal the source list. A JSON demand without source_policy deserializes as Pooled.
Delegated access grants
Section titled “Delegated access grants”An access grant (ResourceAccessGrantV1) lets a grantee draw on a grantor custodian’s stock without a prior transfer. It records:
grantor_custodianandgrantee;- the exact
resource_revisionandunit_revision; cap_quantity, the most the grant can ever supply;- the window
valid_from..valid_until(end exclusive); authority_evidence, the exact record version that justifies the grant, such as your application’s accepted requisition record.
The grantor issues it as a tracked command:
use canwu_api::{CommandEnvelope, CommandRequest, CommandRequestId, Issuer};use canwu_resource::{ ResourceCommandV1, ResourceIssueAccessGrantRequestV1, ResourceOperationRequestV1, resource_command,};
let command = resource_command(&ResourceCommandV1 { subject: grant.grantor_custodian.clone(), request: ResourceOperationRequestV1::IssueAccessGrant(ResourceIssueAccessGrantRequestV1 { operation_key, grant, // a ResourceAccessGrantV1 }),})?;canwu.enqueue_command( now, 0, CommandRequest::new( CommandRequestId::new(41), canwu.revision(), CommandEnvelope::new(Issuer::Actor(grantor_person), command).at_time(now), ),)?;How a grant is used:
- Issue. The grantor custodian submits
IssueAccessGrantas a tracked command. The authority evidence must be an available exact record version. Adapter ingress cannot issue or revoke grants. - Demand. The grantee submits a demand with
ResourceDemandSourcePolicyV1::Granted { grant_id, accounts }. The grant must be active and name the requester as grantee; the resource and unit must match; the demand’s due and expiry times must fall inside the grant window; every listed account must be open and custodied by the grantor. - Allocate. Allocation checks the grant before it divides scarce supply among demands. A revoked or out-of-window grant supplies nothing, and no allocation exceeds the remaining cap. Supply comes only from the listed accounts.
- Debit. The grantee debits the allocation, by consumption or by starting a transfer or exchange leg, under its own completion lease. A lease held by anyone else, including the grantor, cannot debit a granted allocation. The debit settles only in a boundary whose time equals the lease’s certified time, and the grant window must contain that time.
Accounting: ResourceAccessGrantRecordV1 keeps cap_quantity = remaining + reserved + debited. Allocation moves quantity from remaining to reserved, and the debit moves it to debited, so each unit is charged once. A released or expired reservation returns its quantity to remaining. Expired reservations of a granted demand are released by the next allocation pass for the grantee, which the host queues with enqueue_resource_allocation.
Transfers: a transfer started from a granted allocation records its access_grant, and the grantee then controls its cancellation, return, and loss. Local acceptance still belongs to the destination custodian.
Revocation: the grantor submits RevokeAccessGrant with the expected grant revision. Once anything has been reserved or debited under the grant, revocation is refused, so choose the cap and window to fit the delegation.
Limits and reads: grants, including revoked ones, stay in hot state, up to 4,096 per resource state (MAX_RESOURCE_ACCESS_GRANTS). Control who may issue them as you control account creation. resource_access_grant_status(canwu, holder, grant_id) returns a grant and its accounting to its grantor or grantee.
For example, a requisition flow in your application could first record the population owner’s acceptance. The owner then issues a grant citing that record, the army submits a Granted demand, and the army’s own lease debits the allocation. The access-grant test issues grants, allocates under them, debits by transfer, and revokes them through the public API.
Account-level loss
Section titled “Account-level loss”ResourceOperationRequestV1::RecordLoss(ResourceAccountLossRequestV1) records spoilage, theft, or any other loss directly on one account. The request carries a loss_id, the account’s expected revision, a quantity, a cause, allow_protected, a time, and a completion certificate.
- It settles as a
ResourceLosswithaccount: Some(..)andtransfer: None, counted as admitted loss in the conservation totals. - The debit leaves reserved stock untouched and respects the protected floor unless
allow_protectedis set. A stale account revision is rejected. - As a tracked command it must come from the account’s custodian. Through adapter ingress, the cause must be the provider’s exact source record.
- Holder reports include
ResourceLossObservationV1entries, visible under the same rules as transfer details.
Atomic exchange
Section titled “Atomic exchange”ResourceOperationRequestV1::BeginExchange(ResourceExchangeStartRequestV1) starts two transfers together: both are created, or neither is. The single outcome lists both transfer IDs in cited_transfers, whether it applied or was rejected.
- The parties agree on
ResourceExchangeTermsV1: the exchange operation key and, for each leg, its transfer ID, exact allocation, and destination. ResourceExchangeTermsV1::leg_operation_keysderives each leg’s operation key from the digest of those terms.- Each leg’s source custodian (or grantee, for a granted leg) acquires a completion lease for that leg’s derived key. The lease therefore consents to the whole exchange and fits no other terms.
- The debit holder of
leg_asubmits the exchange as a tracked command.
The exchange has no certificate of its own. After the start, the two transfers settle independently, so one may be accepted while the other is lost or returned. Each side spends only stock it holds or may draw under a grant.
Local acceptance
Section titled “Local acceptance”ResourceTransferDispositionV1::AcceptLocal { destination, expected_destination_revision, handover_evidence } settles a transfer without a transport execution, for example a handover inside one warehouse or town. It applies only while the transfer is PendingDispatch with no transport link, and only when both accounts declare the same ResourceAccount::place_scope.
- The scenario declares
place_scopewhen it installs an account, and the value stays fixed. Canwu does not infer it. The default isNone, which leaves local acceptance unavailable for that account. A trackedCreateAccountcommand cannot set it. - A missing or mismatched scope is rejected as
invalid_definition; a transfer with a transport link, asinvalid_lifecycle. - The terminal certificate locks the exact handover record. As a tracked command,
AcceptLocalmust come from the destination custodian; through adapter ingress, the handover record must be the provider’s source.
Production output
Section titled “Production output”Realized output
Section titled “Realized output”ProductionOperation::CompleteExecution may carry realized_output_per_mille and realization_evidence. The ratio is in thousandths of the process’s nominal output; None means 1,000. At completion, every output settlement quantity is scaled by the ratio and rounded down, and both values are stored on the execution. The resource credit and the output acknowledgement then settle exactly the scaled quantities.
- A ratio other than 1,000 needs an exact evidence record version of a kind listed in the process revision’s
realization_evidence_kinds. The list is empty by default, so a default process accepts only nominal output. Evidence sent with the nominal ratio is rejected. - The ratio must be positive and must not scale any output leg to zero. To record a total loss, cancel the work order.
- The ratio may not exceed the process revision’s
max_realized_per_mille, which defaults to 1,000; set it higher to accept evidenced yields above nominal. - Holder, lifecycle, ratio, and kind rules run before the evidence record is looked up, so a rejection reveals nothing about whether another record exists.
How output settles
Section titled “How output settles”After an execution completes, a phase-12 system in canwu-production pins the current production runtime record version on the execution as its output_source and schedules one output batch to canwu-resource. The resource plugin credits each output account through its production output batch ingress, which requires the credit to cite exactly that pinned source. The generic adapter ingress rejects production credits.
Authorize a custom consumer
Section titled “Authorize a custom consumer”A consumer plugin of your own can consume allocations through the ResourceConsumptionIntentV1 contract:
- In your plugin’s own active domain record, keep a top-level
resource_consumption_intentsmap. Each key is the sealed intent’sid. No callback or reference-provider name is needed. - Pass that record kind to
ResourcePlugin::newso the resource adapter may read the exact source. - Before submitting, complete the allocation and completion-lease steps.
- Submit
ResourceOperationRequestV1::Consumethrough adapter ingress (enqueue_resource_adapter_operation). Itsconsumer_evidencemust equal the provider’s exact current source record, and the completion certificate must lock that source and use the same operation key. The usual holder, participant, time, revision, and lease checks apply.
The resource plugin accepts the consumption only when exactly one Authorized intent in the map matches the whole allocation leg, the demand and account revisions, the consumption ID, and the operation key. Consuming part of a leg is outside this contract. The map may hold at most max_operation_outcomes entries (from the resource state’s ResourceLimitsV1); a larger map is rejected before any entry is decoded. Missing or malformed maps, identity or digest mismatches, and retired, ambiguous, or mismatched intents are rejected, and nothing is debited. A sealed digest proves the intent’s content is consistent; the permission comes from your plugin authorizing it.
Your plugin owns authorizing and retiring intents and any local consequences after the resource receipt. canwu-resource owns the conserved balance and duplicate-operation handling. The force-supply reference consumer (canwu-force-supply-reference) keeps its own adapter, which also accepts an earlier version of its source record; custom consumers use the current-source contract above.
Versions and saves
Section titled “Versions and saves”Pin exact versions of canwu-api, canwu-resource, canwu-production, and the plugin semantic hashes from one release line. Loading restores the saved Canwu snapshot and revalidates extension records against the plugins you pass. See save compatibility.
Continue with the production, resources, and military supply case and the mechanism decision.