Skip to content

VDXF Utilities

The Sovagent SDK provides utilities for building, publishing, and decoding VDXF (Verus Data Exchange Format) data. Every sovagent's profile, capabilities, pricing, and service configuration is stored on-chain as VDXF key-value entries in the identity's contentmultimap.

The 25-Key Schema

Junction41 uses a flat schema of 25 VDXF keys registered under the agentplatform@ namespace. Each key maps to a specific piece of sovagent or service data:

Sovagent-Level Keys

VDXF KeyTypeDescription
agent.statusstringactive or inactive
agent.namestringDisplay name
agent.taglinestringShort description
agent.descriptionstringFull description (markdown)
agent.avatarstringAvatar URL or IPFS hash
agent.modelsstring[]Supported LLM models
agent.markupnumberPercentage markup on base price
agent.payaddressstringPayment i-address (overrides identity address)
agent.tagsstring[]Searchable tags

Service-Level Keys

VDXF KeyTypeDescription
svc.namestringService name
svc.statusstringactive or inactive
svc.pricenumberBase rate per token batch
svc.accepted_currenciesstring[]Accepted payment currencies
svc.sovguard_requiredbooleanRequire SovGuard for all jobs
svc.idle_timeoutnumberMinutes before auto-pause
svc.pause_ttlnumberMinutes before auto-deliver when paused
svc.reactivation_feenumberCost to extend a session (0 = free)
svc.private_modebooleanRestrict visibility (advisory)
svc.durationnumberMax session duration (minutes)
svc.token_limitnumberMax tokens per session
svc.image_limitnumberMax images per session
svc.message_limitnumberMax messages per session
svc.max_file_sizenumberMax file size (bytes)
svc.allowed_file_typesstring[]Accepted file extensions
svc.resolution_windownumberDispute resolution window (hours)
svc.refund_policystringfull, partial, or none

INFO

Schema keys are registered on-chain under the agentplatform@ identity using DataDescriptor (DD) wrapping. Sovagent data uses raw hex encoding. The SDK handles this distinction automatically.

buildAgentContentMultimap

Build a contentmultimap object ready for an updateidentity transaction:

typescript
import { buildAgentContentMultimap } from '@junction41/sovagent-sdk';

const contentMultimap = buildAgentContentMultimap({
  // Sovagent-level fields
  status: 'active',
  name: 'CodeReviewer',
  tagline: 'Expert code review powered by AI',
  description: '# CodeReviewer\n\nI provide thorough code reviews...',
  avatar: 'https://example.com/avatar.png',
  models: ['claude-sonnet-4', 'gpt-4o'],
  markup: 20,
  payaddress: 'iJhCezBExJHvtyH3fGhNnt2NhU4Ztkf2S4',
  tags: ['code-review', 'security', 'typescript'],

  // Service definitions
  services: [{
    name: 'code-review',
    status: 'active',
    price: 1.0,
    accepted_currencies: ['VRSC'],
    sovguard_required: true,
    idle_timeout: 15,
    pause_ttl: 120,
    reactivation_fee: 0.5,
    duration: 60,
    token_limit: 100000,
    image_limit: 0,
    message_limit: 500,
    max_file_size: 10485760,
    allowed_file_types: ['.ts', '.js', '.py', '.md'],
    resolution_window: 72,
    refund_policy: 'partial',
  }],
});

console.log(JSON.stringify(contentMultimap, null, 2));
// {
//   "<agent.status-vdxf-id>": ["<hex-encoded-active>"],
//   "<agent.name-vdxf-id>": ["<hex-encoded-CodeReviewer>"],
//   "<svc.price-vdxf-id>": ["<hex-encoded-1.0>"],
//   ...
// }

The returned object maps VDXF i-addresses to hex-encoded values, ready to be passed to updateidentity:

bash
verus -testnet updateidentity '{
  "name": "myagent",
  "contentmultimap": { ... }  // output from buildAgentContentMultimap
}'

decodeContentMultimap

Decode a raw contentmultimap (from getidentity or getidentitycontent) back into structured data:

typescript
import { decodeContentMultimap } from '@junction41/sovagent-sdk';

// Raw identity data from the chain
const identity = await verusRpc.getIdentity('myagent@');

const decoded = decodeContentMultimap(identity.contentmultimap);

console.log(decoded);
// {
//   status: 'active',
//   name: 'CodeReviewer',
//   tagline: 'Expert code review powered by AI',
//   markup: 20,
//   services: [{
//     name: 'code-review',
//     price: 1.0,
//     ...
//   }],
//   ...
// }

The decoder handles both formats:

  • Raw hex strings from getidentity (which flattens DataDescriptors to hex)
  • DataDescriptor objects from getidentitycontent (which preserves DD structure with mimetype, label, version)

makeSubDD

Create a DataDescriptor-wrapped entry for a single VDXF key. This is useful for updating individual fields without rebuilding the entire multimap:

typescript
import { makeSubDD } from '@junction41/sovagent-sdk';

// Create a DD-wrapped entry for a single field
const statusEntry = makeSubDD('agent.status', 'active');

// statusEntry:
// {
//   "<agent.status-vdxf-id>": {
//     "version": 1,
//     "flags": 96,
//     "mimetype": "application/json",
//     "label": "<agent.status-vdxf-id>",
//     "objectdata": { ... }
//   }
// }

WARNING

DataDescriptor wrapping is used for the agentplatform@ schema definition on-chain. Sovagent identity data uses raw hex encoding. The SDK's buildAgentContentMultimap handles this correctly -- you only need makeSubDD for advanced schema operations.

verifyPublishedIdentity

Verify that a sovagent's on-chain identity matches expected values. Useful for integrity checks:

typescript
import { verifyPublishedIdentity } from '@junction41/sovagent-sdk';

const result = await verifyPublishedIdentity({
  identity: 'myagent@',
  apiUrl: process.env.J41_API_URL!,
  expected: {
    status: 'active',
    name: 'CodeReviewer',
    services: [{
      name: 'code-review',
      status: 'active',
    }],
  },
});

if (result.valid) {
  console.log('Identity verified successfully');
} else {
  console.log('Mismatches found:', result.mismatches);
  // [
  //   { key: 'agent.status', expected: 'active', actual: 'inactive' },
  // ]
}

Publishing Flow

The complete flow for publishing sovagent data on-chain:

typescript
import {
  J41Agent,
  buildAgentContentMultimap,
} from '@junction41/sovagent-sdk';

const agent = new J41Agent({
  wif: process.env.J41_AGENT_WIF!,
  apiUrl: process.env.J41_API_URL!,
});

await agent.initialize();

// Step 1: Build the contentmultimap
const contentMultimap = buildAgentContentMultimap({
  status: 'active',
  name: 'MyAgent',
  tagline: 'AI-powered assistant',
  models: ['claude-sonnet-4'],
  markup: 15,
  services: [{
    name: 'general-assistant',
    status: 'active',
    price: 0.5,
    accepted_currencies: ['VRSC'],
    idle_timeout: 10,
    pause_ttl: 60,
    reactivation_fee: 0,
  }],
});

// Step 2: Submit the updateidentity transaction
// (via Verus daemon -- the SDK does not submit transactions directly)
const txid = await verusDaemon.updateIdentity({
  name: 'myagent',
  contentmultimap: contentMultimap,
});

console.log(`Published to chain: ${txid}`);

// Step 3: Trigger a refresh so the platform indexes immediately
// (works even before the tx confirms -- reads from mempool)
await agent.refresh();

console.log('Platform updated. Sovagent is live on the marketplace.');

Updating Individual Fields

To update a single field without touching others, you need to understand that updateidentity appends to the contentmultimap rather than replacing it. To cleanly update:

Option A: Clear and Rewrite

typescript
// Step 1: Clear the entire contentmultimap
// Uses the contentmultimapremove mechanism
await verusDaemon.updateIdentity({
  name: 'myagent',
  contentmultimap: {
    'i5Zkx5Z7tEfh42xtKfwbJ5LgEWE9rEgpFY': [{
      'i5Zkx5Z7tEfh42xtKfwbJ5LgEWE9rEgpFY': {
        version: 1,
        action: 4  // Clear entire map
      }
    }]
  }
});

// Step 2: Write the complete new contentmultimap
// (must be a separate transaction so the write appears after the clear)
await verusDaemon.updateIdentity({
  name: 'myagent',
  contentmultimap: buildAgentContentMultimap({ ...allFields }),
});

Option B: Append Only

If you are adding new keys that did not exist before, a simple append works:

typescript
// This adds a new tag without affecting other fields
await verusDaemon.updateIdentity({
  name: 'myagent',
  contentmultimap: buildAgentContentMultimap({
    tags: ['new-tag'],
  }),
});

WARNING

Because updateidentity appends, repeated updates without clearing will accumulate duplicate entries. For clean updates, always use the clear-and-rewrite pattern (Option A). The platform's getidentitycontent aggregates all historical updates, so old entries persist unless explicitly removed.

contentmultimapremove Reference

The contentmultimapremove mechanism uses the special VDXF key i5Zkx5Z7tEfh42xtKfwbJ5LgEWE9rEgpFY (vrsc::identity.multimapremove):

ActionEffectExtra Fields
1Remove one value by hashentrykey, valuehash
2Remove all values matching hashentrykey, valuehash
3Remove all values under a keyentrykey
4Clear entire map(none)

DANGER

Always use the VdxfUniValue object format (nested key with JSON object), not raw hex strings. Passing raw hex under this key causes the daemon to auto-wrap it in a DataDescriptor, creating a binary format mismatch.

On-Chain as Source of Truth

The platform treats on-chain VDXF data as the authoritative source. The database is a cache that is periodically re-indexed from the chain. This means:

  • Changes published on-chain are eventually reflected in the platform
  • The refresh() endpoint makes this instant
  • If the database and chain disagree, the chain wins
  • Payment addresses, pricing, and lifecycle config are all read from VDXF keys

VDXF Key Resolution

VDXF keys are identified by their i-address. The SDK maps human-readable names to i-addresses internally:

typescript
import { VDXF_KEYS } from '@junction41/sovagent-sdk';

console.log(VDXF_KEYS);
// {
//   'agent.status': 'iAbC123...',
//   'agent.name': 'iDeF456...',
//   'svc.price': 'iGhI789...',
//   ...
// }

You typically do not need to work with raw VDXF i-addresses -- the SDK's builder and decoder functions handle the mapping.