Soulidity

Resources

Skills & Docs Revisions

Soul Skills are versioned ZIP bundles stored on Walrus and indexed on-chain by skill name and version index. Each skill has an independent revision history, and versions can be marked public or private.

📊 Protocol Stats

On-Chain Structure

// Shared object — one per Soul
public struct SoulSkills has key {
    id: UID,
    soul_id: ID,
    skills: Table<String, vector<SkillSlot>>,
    skill_count: u64,              // unique skill names
}

public struct SkillSlot has copy, drop, store {
    blob_object_id: ID,
    is_public: bool,
    deleted: bool,
    created_at_ms: u64,
}

// Walrus Blob stored as dynamic object field
public struct SkillBlobKey has copy, drop, store {
    skill_name: String,
    version_index: u64,            // 0-based index into slot vector
}

The skills table maps a skillName string to a vector of SkillSlot values. Each append pushes a new slot; the index into the vector is the versionIndex. Both are required to address a specific version.

ZIP-Only Upload

Skills must be uploaded as .zip archives. The upload validation layer enforces this before the Walrus upload occurs. Inside the ZIP, a SKILL.md file at the root is required.

Required: SKILL.md frontmatter
---
name: my-skill-name       # becomes the on-chain skillName key
version: 1.0.0            # human-readable version label
description: |
  What this skill does.
---

# Skill content here

The name field from the frontmatter becomes the skillName used as the table key on-chain. If the name already exists in the SoulSkillstable, the new version is appended to that skill's vector (versionIndex increments). If it is new, a fresh entry is created (versionIndex = 0).

Public vs Private Visibility

Each version is individually marked is_public at upload time. This controls whether Seal encryption is required to read the blob.

VisibilityAccess model
publicWalrus blob URL is returned directly. No Seal session required. Anyone can download.
privateSeal-encrypted. Requires owner wallet or active SCOPE_SKILLS SoulGrant. Client must build approval TX and run Seal decryption.

Visibility is immutable after mint. To change visibility you must append a new version with the desired setting.

Soft Delete

The owner (or a holder of SCOPE_SKILLS grant) can call skills::delete_version_as_owner or delete_version_as_granted_agent. This sets deleted = true on the slot — it does not remove the slot from the vector or free the Walrus blob.

  • The versionIndex is preserved. Attempting to read a deleted version returns an error from both the Move approval functions and the API.
  • A SkillVersionDeleted event is emitted with skills_id, skill_name, version_index, and deleted_by.
  • Re-deleting an already-deleted slot aborts with ESkillVersionDeleted.

Skills Access API

GET /api/souls/[id]/skills/[skillName]/versions/[versionIndex]/access

// Public response
{
  visibility: "public",
  artifact: { walrusBlobUrl, walrusBlobId, blobObjectId }
}

// Private response
{
  visibility: "private",
  artifact: { walrusBlobUrl, walrusBlobId, blobObjectId },
  accessPolicy: {
    packageId, stateObjectId, skillsObjectId,
    skillName, versionIndex,
    moduleName: "skills" | "content_access",
    functionName:
      "seal_approve_private_read_owner"
      | "seal_approve_private_read_granted_agent"
      | "seal_approve_skill_allowlisted",
    soulGrantObjectId: string | null,
    accessListOnChainId?: string,
    documentIdHex: string,
  },
  seal: { ... },
  sealSidecar: { ... },
  viewerAddress, accessKind, sessionTtlMin
}

The client SDK function fetchSkillAccess in web/lib/soulidity/skill-access.ts handles this request and returns a typed SkillAccessResponse. For private versions, call loadDecryptedPrivateSkillVersion to run the full Seal decryption flow and receive the plaintext ZIP bytes.