Sidecars

Switchyard

Allocates execution buckets to positions and records close, sweep and return to reuse.

0.1.0-alpha.3Alpha capabilityDeclared targets: Windows, Androiduk.capp.sidecar.solana-switchyard

What it does

Switchyard records which off-chain execution bucket belongs to a position and when that resource is eligible for reuse. Use it for a product that needs a visible allocation, open/close lifecycle and explicit evidence of return/sweep.

A bucket is a record, not a wallet key, on-chain vault or custody service. Allocating it does not create/fund a wallet or move assets. Applications or qualified services perform the external work and then record its observed lifecycle.

Use independently

Select uk.capp.sidecar.solana-switchyard in local-allocation mode. No wallet, RPC, Arena or Edge is required. The owning API is window.capp.solana.switchyard; use the portable client for the same methods.

Retain your profile, vault, client-position, market and side identity. The local allocation key is derived from these fields. Keep the returned railId for subsequent reads/transitions instead of guessing which record a position label identifies.

Provider dependencies

ModeDependencyApplication responsibility
local-allocationNoneSupply stable position identity and genuine evidence of any external lifecycle step.
external-adapterConfigured allocation/rail serviceQualify its concurrency, identity, custody and evidence semantics separately.
Optional Arena integrationArena plus your application glueKeep simulated close/sweep evidence explicitly simulated.

Edge does not automatically call Switchyard during payment preparation. An application that needs allocation must orchestrate it deliberately. A local executionWallet field is just an identity string; it is not validation that your app controls the address.

First successful allocation

  1. Define stable profileId, vaultId, clientPositionId, market and side values for one position lifecycle.
  2. Allocate with the intended public execution-wallet identity, or omit it when your own workflow still needs funding/setup.
  3. Retain the returned railId, allocation key and state. Repeat identical input to confirm idempotence.
  4. Use the transition table only after the corresponding application/provider event has occurred.
  5. Make a bucket reusable only after closed-position and sweep evidence. A new position gets a new identity and record even if it reuses the same external wallet resource.

Allocate and inspect without moving funds

Copy the client module alongside this module. Supply your actual public execution-wallet identity. The helper is inert until explicitly called and records no funding or trading result.

js
import {createSolanaClient} from './solana-client.mjs';
const client = createSolanaClient(() => window.capp?.solana);

export async function allocatePositionBucket({profileId, vaultId, clientPositionId, market, side, executionWallet}) {
  const status = await client.call('switchyard', 'status');
  if (!status.ready || status.providerMode !== 'local-allocation') {
    throw new Error('A ready local Switchyard is required');
  }
  const bucket = await client.call('switchyard', 'allocate', {
    profileId, vaultId, clientPositionId, market, side, executionWallet
  });
  if (bucket.offChainAllocation !== true || bucket.fundsMoved !== false) {
    throw new Error('Unexpected allocation boundary');
  }
  return client.call('switchyard', 'read', {railId: bucket.railId});
}

Expect allocated when a wallet identity was supplied, or rail_wallet_needs_funding when it was omitted. Replaying identical input returns the same record. Changing the wallet under the same allocation identity is a conflict, not an update: this API does not provide a method to attach a different wallet to that record later.

Lifecycle and reuse

Current stateSupported next states
allocatedrail_wallet_needs_funding, rail_wallet_funded, position_request_created
rail_wallet_needs_fundingrail_wallet_funded
rail_wallet_fundedposition_request_created, sweep_requested
position_request_createdposition_open_pending
position_open_pendingposition_open, position_closed
position_openposition_close_pending
position_close_pendingposition_closed
position_closedsweep_requested
sweep_requestedrail_bucket_reusable
rail_bucket_reusableTerminal record; preserve it for history

Transitions into rail_wallet_funded, position_open, position_closed or rail_bucket_reusable require an evidenceDigest. The digest is a caller-supplied reference to evidence, not on-chain verification by Switchyard. Retain the evidence itself at its proper owner, and never substitute an arbitrary hash to release a real resource.

With requireUniqueExecutionWallet enabled, an external wallet identity cannot be assigned to another active record. Reaching rail_bucket_reusable permits its reuse for a new allocation identity; it does not reset/delete the old position. Calling allocate() again with the original identity returns its old terminal record.

Scope and limitations

A bucket is a record, not an on-chain vault. Changing its lifecycle does not move funds. Position identity remains historical when a resource is reused.

The store and wallet-identity guard are local to this app/origin. They are not a distributed allocator or a chain lock. Local rows do not contain private keys or threshold shares, but account/position identity can still be private business information. Inspect manifest() before sharing it.

Each local lifecycle is bounded to 128 entries, alongside configured bucket/store capacity. There is no force-release/reset method in the public API. Keep unresolved records intact when an external operation is uncertain.

Application API

The owning interface is window.capp.solana.switchyard. The portable client calls the same methods. This table describes main arguments, not a stricter typed guarantee than the current provider implements. Exact transaction/provider bindings must be preserved; unsupported state or arguments are rejected.

js
import {createSolanaClient} from './solana-client.mjs';
const client = createSolanaClient(() => window.capp?.solana);
const status = await client.call('switchyard', 'status');

status

client.call('switchyard', 'status', input)

Main input: No arguments required.

Read allocation-provider readiness and lifecycle constraints.

allocate

client.call('switchyard', 'allocate', input)

Main input: clientPositionId; optional profileId, vaultId, market, side, executionWallet.

Allocate an idempotent off-chain execution bucket. Does not fund a wallet or move assets.

read

client.call('switchyard', 'read', input)

Main input: railId, or a uniquely identifying clientPositionId scope.

Read one execution bucket; ambiguous results are rejected.

transition

client.call('switchyard', 'transition', input)

Main input: railId, state; evidenceDigest for funded, open, closed or reusable transitions.

Record a supported lifecycle transition with supplied evidence. External effects remain the application/provider responsibility.

manifest

client.call('switchyard', 'manifest', input)

Main input: No arguments required.

Read active and reusable buckets. Private keys and raw shares are not included.

Troubleshooting

Symptom or errorNext step
WALLET_IN_USERead the active rail named in the error and reconcile it before reassigning the resource.
ID_CONFLICTRecover the original position allocation; do not change its execution wallet in place.
NOT_FOUND / ambiguous position readUse the exact retained railId, or supply a uniquely identifying profile/vault scope.
STATEFollow the supported lifecycle; a requested close is not a recorded close/sweep.
EVIDENCESupply the digest of genuine retained lifecycle evidence, preserving its simulation/live classification.
CAPACITY / storage errorPreserve history and restore storage capacity; do not delete a pending allocation.

Verify your integration

Test identical allocation, changed wallet, two active records competing for a wallet, a missing evidence digest and reuse after a complete recorded close/sweep. Confirm that no local transition performs or claims an unobserved transfer.

Integration recipes

Composition · Devnet payment · Recovery · Typed client · Schemas

These schemas describe payload shape, not evidence of approval or successful execution. Match the version in your payload; older versions remain listed for compatibility.

Configuration reference

These are the selected package's configuration fields. String credential values and installation-specific defaults are never published here. A field's presence does not bypass its runtime policy.

FieldTypeMeaning and constraints
providerModeselectSelect the implementation this build will use. Configuring an endpoint does not verify its connection or behavior.. Choices: local-allocation, external-adapter
maxBucketsnumberMaximum local records. At the limit, new records stop; existing history is preserved. Increase the limit or archive and reconcile the workspace.. Bounds: 8 to 4096
requireUniqueExecutionWalletcheckboxReject active allocations that try to reuse the same execution-wallet identity.
externalAdapterUrltextAbsolute HTTPS endpoint, or loopback HTTP for development. Keep private credentials on the backend; packaged configuration is visible to users.

Agent interfaces

These names are declared by the package for host/Agent integration, not JavaScript methods to invoke directly. The host must register a tool, grant its scope and enforce its approval policy. A manifest declaration alone does not prove availability in every Agent catalogue. Prefer the application API above when integrating your own app.

ToolDeclared riskApproval requirement
solana.switchyard.statuslowmode-policy

Component changelog

baseline0.1.0-alpha.3Permalink

Switchyard

Allocates execution buckets to positions and records close, sweep and return to reuse.

Documentation snapshot, not a historical release date.
View component notes →
↑ ↓ to navigate · Enter to open · Esc to close. Search stays in your browser.