Skip to content

Replace vetkd skill with a new vetkeys covering BLS, IBE, and timelock.#162

Open
andreacerulli wants to merge 1 commit intomainfrom
add-vetkeys-skill
Open

Replace vetkd skill with a new vetkeys covering BLS, IBE, and timelock.#162
andreacerulli wants to merge 1 commit intomainfrom
add-vetkeys-skill

Conversation

@andreacerulli
Copy link
Copy Markdown
Collaborator

The original vetkd skill was overly low-level and unnecessarily complex. In contrast, the new vetkeys skill focuses on three practical, high-impact primitives: BLS threshold signatures, identity-based encryption (IBE), and timelock encryption.

I evaluated the skill by building example dApps for all three primitives, and the results seemed reasonable. I also compared the experience of developing a Random Beacon dApp with and without the skill. Using the skill produced a more advanced implementation with noticeably less effort. Without it, development took significantly longer, the architecture was more cumbersome, and the end result felt comparatively basic.

The old vetkd skill was too low-level and complex. The new vetkeys skill
covers three practical primitives: BLS threshold signatures, identity-based
encryption (IBE), and timelock encryption with compile-tested code examples.
@andreacerulli andreacerulli requested review from a team and JoshDFN as code owners April 10, 2026 09:28
@github-actions
Copy link
Copy Markdown

Skill Validation Report

Validating skill: /home/runner/work/icskills/icskills/skills/vetkeys

Structure

  • Pass: SKILL.md found

Frontmatter

  • Pass: name: "vetkeys" (valid)
  • Pass: description: (439 chars)
  • Pass: license: "Apache-2.0"
  • Pass: compatibility: (16 chars)
  • Pass: metadata: (2 entries)

Markdown

  • Pass: no unclosed code fences found

Tokens

File Tokens
SKILL.md body 4,910
Total 4,910

Content Analysis

Metric Value
Word count 2,158
Code block ratio 0.50
Imperative ratio 0.06
Information density 0.28
Instruction specificity 0.80
Sections 15
List items 23
Code blocks 14

Contamination Analysis

Metric Value
Contamination level medium
Contamination score 0.35
Primary language category systems
Scope breadth 4
  • Warning: Language mismatch: config, javascript, shell (3 categories differ from primary)

Result: passed

Project Checks


WARNINGS (1):
  ⚠ vetkeys/SKILL.md: missing evaluations/vetkeys.json — see CONTRIBUTING.md for evaluation guidance

✓ Project checks passed for 1 skills (1 warnings)

Comment on lines +50 to +51
"@dfinity/agent": "^3.4.0",
"@dfinity/principal": "^3.4.0",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a reason we cannot use the new core library? can we make a new release for the vetkeys lib if it still relies on old dependencies?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think is only because of the vetkeys library. I can look into it.

@marc0olo
Copy link
Copy Markdown
Member

Review: vetkeys skill

Great direction — replacing the old vetkd skill with a focused BLS/IBE/timelock skill and splitting out encrypted-maps is the right architecture. A few things to address before merge:

Description

Remove "verifiable randomness (VRF)" from the description. The skill has no VRF content, so an agent routed here for VRF finds nothing actionable. Add it back only if a VRF section is included.

Cycle costs — precise numbers + missing pitfall

The Key Concepts section uses ~10B and ~26B. These should be exact:

  • key_1 mainnet: 26_153_846_153 cycles
  • test_key_1 mainnet: 10_000_000_000 cycles
  • Both keys locally (PocketIC): 26_153_846_153 cycles — a known deviation; fixing it would require deploying test keys to a dedicated application subnet

The local PocketIC deviation should be a dedicated pitfall, not buried in Key Concepts. This is the exact error that caused issue #156. Recommending 30_000_000_000 as a universal safe buffer (unused cycles are refunded) is the simplest advice for agents.

IBE backend — missing types

"The canister structure is the same as the BLS example" leaves an agent unable to compile. Missing: SendMessageRequest, Message, Inbox struct definitions, the INBOXES StableBTreeMap declaration, and the VetKDDeriveKeyArgs import (used in the snippet but not imported). Either show the full canister or move the IBE example to a references/ file (like encrypted-maps does for Motoko).

IBE frontend — identity undefined

identity.getPrincipal() appears with no explanation of where identity comes from. The encrypted-maps skill handles this correctly — same pattern should apply here.

Timelock — not compilable as-is

decrypt_ciphertexts has Vec<&[u8]> in its signature (lifetime parameters can't appear in a Candid endpoint), has no #[update] annotation, and calls close_expired_lots() which is never defined. This reads as real code but an agent following it will fail to compile. Either provide a complete working example or explicitly mark it as a pseudocode sketch.

Section ordering

Offline public key derivation sits between timelock and icp.yaml, which breaks the reading flow. It belongs inside the IBE section (it's an IBE-specific optimization) or after Deploy as an advanced appendix.

icp.yaml section

The icp-cli skill covers recipes, frontend setup, and network config. This section should collapse to the vetKeys-specific part only:

canisters:
  - name: backend
    init_args: '("test_key_1")'  # Production: '("key_1")'

With a reference: "For full icp.yaml setup, see the icp-cli skill."

Deploy & Verify

Drop icp network start -d and icp deploy backend — those are icp-cli territory. Keep the domain-specific verification calls and expected outputs. Add at least one verification step for IBE and timelock, not just BLS.

Missing: evaluation file

Please add evaluations/vetkeys.json. Suggested content:

{
  "skill": "vetkeys",
  "description": "Tests whether agents use the correct vetKeys primitives, avoid raw vetkd_derive_key for BLS, use the correct TypeScript API, and attach sufficient cycles locally.",

  "output_evals": [
    {
      "name": "BLS signing canister",
      "prompt": "I want my canister to sign messages with threshold BLS so clients can verify signatures without trusting the canister. Show me just the Rust backend update function, no deploy steps.",
      "expected_behaviors": [
        "Uses ic_vetkeys::management_canister::sign_with_bls(), not vetkd_derive_key directly",
        "Captures caller before the async call",
        "Returns the signature bytes",
        "Does NOT manually attach cycles — sign_with_bls handles this"
      ]
    },
    {
      "name": "IBE encrypted messaging",
      "prompt": "How do I encrypt a message to a specific user's principal so only they can decrypt it, without exchanging keys first? Show just the frontend encrypt and decrypt flow.",
      "expected_behaviors": [
        "Uses IbeCiphertext.encrypt() with IbeIdentity.fromPrincipal(recipient)",
        "Uses TransportSecretKey.random(), NOT TransportSecretKey.fromSeed()",
        "Uses transportSecretKey.publicKeyBytes(), NOT publicKey()",
        "Uses EncryptedVetKey.deserialize().decryptAndVerify() to get the IBE private key",
        "Does NOT use toDerivedKeyMaterial() or asDerivedKeyMaterial()"
      ]
    },
    {
      "name": "Sealed-bid auction",
      "prompt": "I want bids in my auction canister to stay encrypted until the auction closes, then the canister reveals them. Show the high-level approach and the canister-side decryption logic.",
      "expected_behaviors": [
        "Uses timelock/IBE pattern: identity is the auction or lot ID",
        "Clients encrypt with IbeCiphertext.encrypt() using IbeIdentity.fromBytes(lotId)",
        "Canister decrypts after event using vetkd_derive_key with the event ID as input",
        "Uses a timer (set_timer_interval) to trigger decryption after close",
        "Notes that timers must be re-registered in post_upgrade"
      ]
    },
    {
      "name": "Adversarial: insufficient cycles locally",
      "prompt": "My vetkd_derive_key call fails locally with 'not enough cycles attached' even though I sent 10_000_000_000. What's wrong?",
      "expected_behaviors": [
        "Explains that PocketIC charges 26_153_846_153 cycles for all key names locally",
        "States that test_key_1 only costs 10_000_000_000 on mainnet, not locally",
        "Recommends sending 30_000_000_000 as a safe buffer for both environments",
        "Notes that unused cycles are refunded"
      ]
    }
  ],

  "trigger_evals": {
    "description": "Queries to test whether the vetkeys skill activates correctly.",
    "should_trigger": [
      "I want threshold BLS signatures in my canister",
      "How do I implement identity-based encryption on ICP?",
      "Build a sealed-bid auction on the Internet Computer",
      "I need timelock encryption — data revealed only after an event",
      "How do I encrypt to a recipient without knowing their public key?",
      "Verifiable randomness using vetKeys on ICP"
    ],
    "should_not_trigger": [
      "I want to build a password manager on ICP",
      "How do I add encrypted storage to my canister?",
      "I need user authentication for my dapp",
      "How does stable memory work?",
      "What is EncryptedMaps?"
    ]
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants