Compositions
Each contract_version is self-contained. It declares which config blocks it requires and runs independently against any API server. No embodiment's SDK client imports another embodiment's shyconfig at runtime — compositions are wired at the application layer, not inside the clients.
There are four composition patterns:
- Standalone — a single shyconfig, a single client, no sub-clients, no rail slot.
- Extension — a single shyconfig that adds code on top of one shyware core. The extension cannot meaningfully exist without the core it extends.
- Pair-product — a single shyconfig that combines two shyware cores into one unified surface. Neither core is optional; the product is the combination.
- Bring-your-own-rail — a single shyconfig with a pluggable rail slot. Can exist without shywire — operators fill the slot with shywire or any external system. Two clients when shywire is used; application layer bridges them.
Dependency map
| Contract version | Required blocks | Composition type | Notes |
|---|---|---|---|
shyvoting-v1 | receipts, deployment | standalone or optional rail | identity.provider must not be "none"; can be paired with shares or contracts as a governance gate |
shywire-v1 | wire | standalone or optional rail | identity.provider: "wallet"; built-in anonymous transfer rail — use it or bring your own |
shystore-v1 | store | standalone or optional rail | anon_layer.shyPayload or shyIDV required; optionally pairs with contracts or shares as a sealed evidence vault |
shychat-v1 | messaging | standalone or optional rail | identity.surface_model: "mail"|"chat" required; optionally pairs with contracts, lots, shares, or custody as a notification rail |
shybrowser-v1 | sealer | standalone | No required_flows check |
shystream-v1 | store + stream | extension of shystore-v1 | Adds live segment delivery and queue on top of sealed store |
shybets-v1 | wire + bets | extension of shywire-v1 | Adds event markets, order books, and settlement logic on top of wire |
shyrest-v1 | store + messaging | pair-product — shystore-v1 + shychat-v1 | Store and chat combined as one unified surface; neither is the base |
shylots-v1 | custody + wire + lots | pair-product — shycustody-v1 + shywire-v1 | Custody and wire combined as one unified surface; lots is the domain |
shycontracts-v1 | contracts | bring-your-own-rail | Rail slot: declare transfer_layer: "shywire" or omit and supply your own |
shycustody-v1 | custody + store + stream | bring-your-own-rail | Rail slot: declare transfer_layer: "shywire" or omit and supply your own |
shyshares-v1 | governance + execution | bring-your-own-rail | Rail slot: declare transfer_layer: "shywire" or omit and supply your own |
Standalone embodiments
Single shyconfig, single client, no sub-clients, no rails:
shyvoting-v1
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/votingClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, runtimeSignals })
await client.voteSubmission({ scopingId: pollId, payload: 'yes', personId })
Requires receipts + deployment blocks. identity.provider must not be "none". No wire, store, or stream blocks.
shywire-v1
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/wireClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, operatorMode: true })
const { accountCommitment } = await client.registerAccount({ walletAddress })
await client.wireSubmission({ scopingId: assetId, senderCommitment, recipientCommitment, amount })
Requires only the wire block. identity.provider: "wallet".
shystore-v1
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/storeClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, deriveSealerKey, signMessage, getIdentityAttestation })
await client.storeSubmission({ scopingId: bucketId, plaintext, category })
const plain = await client.revealAndDecryptStore({ scopingId, submissionId })
Requires store block. anon_layer.shyPayload: true or anon_layer.shyIDV: true. No transfer rail.
shychat-v1
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/chatClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, deriveSealerKey })
await client.createMailbox({ label, address, routeHint })
await client.queueDispatch({ mailboxId, recipientAddress, subject, body, contentClass })
Requires messaging block. identity.surface_model: "mail" | "chat" and domains.private.console required.
shybrowser-v1
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/browserClient.js'
const client = initializeFromShyConfig(shyconfig, { deriveSealerKey })
await client.storeSealedBrowserData(data, 'browser_session')
await client.submitList2IdentityAttribute(ipAddress, 'ip_address')
Requires only sealer.mode: "sealed_storage". No required_flows check, no anon_layer block required.
Extension compositions
A single shyconfig. The extension adds code and a new domain surface on top of one shyware core. The core's client is managed internally — you never instantiate it separately, and the extension is meaningless without it.
shystream-v1 (extends shystore-v1)
streamClient wraps storeClient internally, deriving it from the same manifest. Stream adds live segment delivery and queue semantics; sealed storage is the foundation.
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/streamClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, deriveSealerKey, signMessage, getIdentityAttestation })
await client.createBucket({ scopingId: 'stream-session-42', allowedCategories: ['stream_event', 'stream_clip'] })
await client.sealLiveSegment({ bucketID: 'stream-session-42', streamID, sequence, segment })
const queue = client.createLiveQueue({ bucketID, streamID, minBatchSize: 3, flushIntervalMs: 4000 })
queue.enqueue({ sequence: 0, segment: chunk })
Requires store + stream blocks. stream.provider, domains.private.console, and signing.required: true enforced. Required flows include biometric_rederive, hop_route_store, hop_route_reveal in addition to the three stream flows.
shybets-v1 (extends shywire-v1)
Bets adds event markets, order book logic, and settlement on top of wire. betsClient creates an internal wireClient from the same shyconfig — the wire block is required but you do not instantiate a wireClient yourself.
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/composites/betsClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders })
await client.registerSettlementAccount({ walletAddress })
await client.createFundingIntent({ amount, destinationNetwork, destinationAddress })
await client.createEvent({ marketId, title, outcomes, closesAt })
await client.placeOrder({ eventId, side: 'back', outcome: 'candidate-a', stake: 500, odds: 2.1, accountCommitment })
await client.settleEvent({ eventId, winningOutcome: 'candidate-a' })
await client.transferStake({ scopingId: assetId, senderCommitment, recipientCommitment, amount })
await client.createPayoutIntent({ amount, accountCommitment, payoutRail, payoutDestination })
const wire = client.getSettlementClient()
Required flows: event_create, order_place, order_book_read, settlement_read, settlement_finalize, reconcile_request. The wire block in the shybets shyconfig is the same structure as a shywire-v1 shyconfig's wire block.
Pair-product compositions
A single shyconfig that combines two shyware cores into one unified surface. Neither core is the "primary" — the product is the combination of both. You do not instantiate sub-clients separately.
shyrest-v1 (shystore-v1 + shychat-v1)
Rest combines store and chat into one surface. restClient derives child manifests automatically — you do not write separate shystore-v1 or shychat-v1 shyconfigs, and neither client is subordinate to the other.
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/restClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, deriveSealerKey, signMessage, getIdentityAttestation })
await client.storeSubmission({ scopingId: bucketId, plaintext, category })
const plain = await client.revealAndDecryptStore({ scopingId, submissionId })
await client.createMailbox({ label, address, routeHint })
await client.queueDispatch({ mailboxId, recipientAddress, subject, body })
const store = client.getStoreClient()
const chat = client.getChatClient()
Requires store + messaging blocks. domains.private.console required. contract_version must be "shyrest-v1" with product_type: "shyrest" — do not set these to shystore-v1 or shychat-v1.
shylots-v1 (shycustody-v1 + shywire-v1)
Lots combines custody and wire into one surface — sealed-bid auctions, lot inventory, and settlement. lotsClient manages both internally from a single shyconfig. Requires custody, wire, and lots blocks.
import { initializeFromShyConfig } from '@co-mission/shyware-sdk/clients/embodiments/lotsClient.js'
const client = initializeFromShyConfig(shyconfig, { getAuthHeaders, operatorMode: true })
const { accountCommitment } = await client.registerBidderAccount({ walletAddress })
await client.createFundingIntent({ amount, destinationNetwork, destinationAddress })
await client.transferBidBond({ senderCommitment, recipientCommitment, amount })
await client.settleAwardTransfer({ senderCommitment, recipientCommitment, amount })
await client.requestLotRedemption({ assetId, accountCommitment, warehouseId, skuClassId, siloAmount, requestedQuantity })
Bring-your-own-rail compositions
Two separately-initialized clients, same api.base_url, application layer bridges them. The rail client is always a shyware embodiment in these examples, but any of these rails — transfer, governance, evidence, notification — can be replaced by a non-shyware equivalent. Neither client enforces the other.
any embodiment + shywire-v1 (transfer rail)
Declare transfer_layer: "shywire" in the primary shyconfig to opt in to anonymous value movement. Omit it to use your own payment system — ACH, Circle, a private ledger, anything. No SDK enforcement either way.
- shycontracts + shywire — fund and execute value-bearing contract terms anonymously.
contracts.transfer_layer: "shywire"required when used.
import { initializeFromShyConfig as initContracts } from '@co-mission/shyware-sdk/clients/embodiments/contractsClient.js'
import { initializeFromShyConfig as initWire } from '@co-mission/shyware-sdk/clients/embodiments/wireClient.js'
const contractsClient = initContracts(contractsShyconfig, { getAuthHeaders })
const wireClient = initWire(wireShyconfig, { getAuthHeaders, operatorMode: true })
await wireClient.registerAccount({ walletAddress })
await wireClient.issueWire({ assetId, accountCommitment, amount })
const { contractId } = await contractsClient.buildRegisterContract({ assetId, parties, contractType })
await contractsClient.submitRegisterContract(txJson)
await contractsClient.executeContract({ contractId, partyCommitment, counterpartyCommitment, sourceRef, amount })
await wireClient.wireSubmission({ scopingId: assetId, senderCommitment, recipientCommitment, amount })
- shycustody + shywire — intramarket silo transfers stay on the custody client; cross-market settlement moves through the wire rail.
custody.transfer_layer: "shywire"required when used.
import { initializeFromShyConfig as initCustody } from '@co-mission/shyware-sdk/clients/embodiments/custodyClient.js'
import { initializeFromShyConfig as initWire } from '@co-mission/shyware-sdk/clients/embodiments/wireClient.js'
const custodyClient = initCustody(custodyShyconfig, { getAuthHeaders, operatorMode: true })
const wireClient = initWire(wireShyconfig, { getAuthHeaders })
await custodyClient.recordIntakeLot({ lotId, policyId, assetId, operatorId, warehouseId, accountCommitment, skuClassId, quantity, mintedAmount, videoSessionRef, evidenceRefs })
await custodyClient.mintSilo({ assetId, accountCommitment, amount })
await custodyClient.transferSilo({ assetId, senderCommitment, recipientCommitment, amount })
await wireClient.wireSubmission({ scopingId: settlementAssetId, senderCommitment, recipientCommitment, amount })
- shyshares + shywire — issue governance tokens and move stake anonymously before and after weighted ballots.
governance.transfer_layer: "shywire"required when used.
import { initializeFromShyConfig as initShares } from '@co-mission/shyware-sdk/clients/embodiments/sharesClient.js'
import { initializeFromShyConfig as initWire } from '@co-mission/shyware-sdk/clients/embodiments/wireClient.js'
const sharesClient = initShares(sharesShyconfig, { getAuthHeaders })
const wireClient = initWire(wireShyconfig, { getAuthHeaders, operatorMode: true })
const { accountCommitment } = await wireClient.registerAccount({ walletAddress })
await wireClient.issueWire({ assetId: governanceTokenId, accountCommitment, amount: 1000 })
await sharesClient.createProposal({ class: 'parameter_change', question: '...', options: ['yes', 'no'], startTime, endTime })
await sharesClient.submitWeightedBallot({ proposal_id: proposalId, choice: 'yes', account_commitment: accountCommitment })
await sharesClient.dispatchAction(actionId, { adapter: 'shywire', adapterPayload: { amount, recipientCommitment } })
- shyvoting + shywire — a vote outcome triggers an anonymous fund release. The wire client is called by the application layer after reading the count-match tally; no config field links the two.
any embodiment + shyvoting-v1 (governance / dispute rail)
No config field enforces the voting rail — the application layer opens a poll and reads the tally when needed.
- shyshares + shyvoting — gate weighted-token proposal execution on an anonymous equal-weight citizen vote; the tally determines whether
dispatchActionis called
import { initializeFromShyConfig as initShares } from '@co-mission/shyware-sdk/clients/embodiments/sharesClient.js'
import { initializeFromShyConfig as initVoting } from '@co-mission/shyware-sdk/clients/embodiments/votingClient.js'
const sharesClient = initShares(sharesShyconfig, { getAuthHeaders })
const votingClient = initVoting(votingShyconfig, { getAuthHeaders, runtimeSignals })
const { pollId } = await votingClient.createPoll({ title: proposalId, options: ['approve', 'reject'] })
const { tally } = await votingClient.getPollTally(pollId)
if (tally.approve > tally.reject) {
await sharesClient.dispatchAction(actionId, { adapter: 'shywire', adapterPayload: { amount, recipientCommitment } })
}
-
shycontracts + shyvoting — require a vote outcome before a party may call
executeContract; the poll result is the ratification gate -
shycustody + shyvoting — submit lot redemption or policy changes to an anonymous arbiter vote before the custody client processes them
-
shybets + shyvoting — when an event outcome is contested, an anonymous arbiter panel votes to settle it; the betting client settles based on the tally
import { initializeFromShyConfig as initBets } from '@co-mission/shyware-sdk/clients/composites/betsClient.js'
import { initializeFromShyConfig as initVoting } from '@co-mission/shyware-sdk/clients/embodiments/votingClient.js'
const betsClient = initBets(betsShyconfig, { getAuthHeaders })
const votingClient = initVoting(arbitrationShyconfig, { getAuthHeaders, runtimeSignals })
const { pollId } = await votingClient.createPoll({ title: `Dispute: ${eventId}`, options: ['original_outcome', 'reversed_outcome'] })
const { tally } = await votingClient.getPollTally(pollId)
const winningOutcome = tally.original_outcome >= tally.reversed_outcome ? originalOutcome : reversedOutcome
await betsClient.settleEvent({ eventId, winningOutcome })
- shylots + shyvoting — when an auction result is disputed, a vote of eligible arbiters resolves it before the lots client executes award transfer
- shywire + shyvoting — a vote outcome authorizes a fund release; the wire client is called only after the count-match tally meets the threshold
any embodiment + shystore-v1 (sealed evidence rail)
shystore-v1 can be added to any deployment to vault sealed documents against the protocol ledger. The store client is initialized separately and the primary client never references it. Note: shycustody-v1 already requires a store block internally and is not listed here.
- shycontracts + shystore — seal contract terms, signed exhibits, or counterparty certifications before activating the contract. The contract record references a
submissionIdfrom the store; neither client links them on-chain.
import { initializeFromShyConfig as initContracts } from '@co-mission/shyware-sdk/clients/embodiments/contractsClient.js'
import { initializeFromShyConfig as initStore } from '@co-mission/shyware-sdk/clients/embodiments/storeClient.js'
const contractsClient = initContracts(contractsShyconfig, { getAuthHeaders })
const storeClient = initStore(storeShyconfig, { getAuthHeaders, deriveSealerKey, signMessage, getIdentityAttestation })
const { submissionId: termsRef } = await storeClient.storeSubmission({
scopingId: contractId, plaintext: JSON.stringify(terms), category: 'contract_terms'
})
await contractsClient.submitRegisterContract(
await contractsClient.buildRegisterContract({ assetId, parties, contractType, termsRef })
)
- shyshares + shystore — seal governance proposal documents, board resolutions, or proxy filings. The proposal record carries a
submissionIdreference; the sealed payload is only accessible to the reconciling authority. - shyvoting + shystore — seal referendum text, candidate disclosures, or supporting documentation before a poll opens. Voters reference the sealed document without the store exposing its content to the canonical ledger.
- shybets + shystore — seal event data, referee reports, or match statistics that anchor the outcome. A sealed
submissionIdcan be cited in a dispute without exposing the underlying data on-chain. - shylots + shystore — seal lot provenance documentation, authenticity certificates, or inspection reports. The custody record references the sealed
submissionId; the payload is only decryptable by the operator or the reconciling authority.
any embodiment + shychat-v1 (counterparty notification rail)
shychat-v1 can be layered onto any deployment that has identified parties. The chat client runs independently — the primary client never calls into it. The application layer decides when to dispatch a notification.
- shycontracts + shychat — notify counterparties when a contract activates, executes, or is rescinded
- shylots + shychat — notify the winning bidder of lot award; notify all participants of auction close
import { initializeFromShyConfig as initLots } from '@co-mission/shyware-sdk/clients/composites/lotsClient.js'
import { initializeFromShyConfig as initChat } from '@co-mission/shyware-sdk/clients/embodiments/chatClient.js'
const lotsClient = initLots(lotsShyconfig, { getAuthHeaders })
const chatClient = initChat(chatShyconfig, { getAuthHeaders, deriveSealerKey })
const settlement = await lotsClient.settleAwardTransfer({ senderCommitment, recipientCommitment, amount })
await chatClient.queueDispatch({
mailboxId: winnerMailboxId,
recipientAddress: winnerAddress,
subject: 'Lot awarded',
body: `Lot ${settlement.lotId} has been awarded. Redemption window opens in 48 hours.`,
contentClass: 'lot_award'
})
- shyshares + shychat — notify members of new proposals, tally results, or dispatched actions
- shycustody + shychat — notify account holders of lot intake, transfer, or redemption events
- shyvoting + shychat — notify participants when a poll opens, closes, or tally becomes available
- shybets + shychat — notify bettors of event creation, settlement, or dispute initiation and resolution
- shywire + shychat — notify the recipient account holder of an incoming transfer