Report from building a production application using ic-vetkeys (EncryptedMaps, KeyManager) with a React frontend. Covers skills used, issues encountered, and suggestions for new skills.
Issues already reported in #96 (Illegal invocation fetch fix, SDK version/import issues, local II deployment, ic_env cookie issues) are excluded here — this report focuses on new findings only.
1. vetkd skill: Rust API mismatches
The skill's Rust code shows types imported from ic_vetkeys::types that actually live in ic_cdk::management_canister:
// Skill shows:
use ic_vetkeys::types::{VetKDCurve, VetKDKeyId};
// Actual:
use ic_cdk::management_canister::{VetKDCurve, VetKDKeyId};
The enum variant name is also wrong:
// Skill shows:
VetKDCurve::Bls12381G2
// Actual (ic-cdk 0.19):
VetKDCurve::Bls12_381_G2
These cause immediate compilation failures.
2. vetkd skill: TypeScript API doesn't match @dfinity/vetkeys v0.4.0
The skill's frontend examples use an API that doesn't exist in the current npm package:
| Skill shows |
Actual @dfinity/vetkeys v0.4.0 API |
TransportSecretKey.fromSeed(seed) |
TransportSecretKey.random() |
tsk.publicKey() |
tsk.publicKeyBytes() |
vetKey.toDerivedKeyMaterial() |
vetKey.asDerivedKeyMaterial() (returns Promise) |
Manual crypto.subtle.importKey("raw", material.data.slice(0,32), ...) |
keyMaterial.deriveAesGcmCryptoKey(domainSep) |
The DerivedKeyMaterial class has a much richer API than documented — encryptMessage(), decryptMessage(), and deriveAesGcmCryptoKey() handle IV generation and key derivation internally. The skill's manual crypto.subtle approach works but is unnecessary and error-prone.
3. vetkd skill: EncryptedMaps not documented
The ic-vetkeys crate's EncryptedMaps<T: AccessControl> is the primary abstraction most developers will want — it wraps KeyManager + StableBTreeMap into a single struct with integrated encrypted storage and access control. It's not mentioned in the skill at all.
Key API details discovered by reading crate source:
EncryptedMaps::init() takes 4 memory regions (not 3 like KeyManager): config, ACL, shared keys, and data storage
KeyId = (Principal, Blob<32>) — owner principal + 32-byte map name
MapKey = Blob<32> — entries within a map use 32-byte keys
EncryptedMapValue = ByteBuf — the crate's own ByteBuf type (not Vec<u8>; has From/Into conversions)
- Owner automatically gets
ReadWriteManage rights
set_user_rights() / remove_user() for granting/revoking access
get_encrypted_values_for_map() checks ACL before returning ciphertext
get_encrypted_vetkey() also checks ACL — access control and encryption are coupled (two independent locks)
get_accessible_shared_map_names() for discovering what's been shared with you
get_all_accessible_encrypted_maps() for bulk retrieval
Example init (not in any skill):
use ic_vetkeys::encrypted_maps::EncryptedMaps;
use ic_vetkeys::types::AccessRights;
ENCRYPTED_MAPS.with(|em| {
*em.borrow_mut() = Some(EncryptedMaps::init(
"my_app_v1", // domain separator
key_id, // VetKDKeyId
mm.get(MemoryId::new(0)), // config memory
mm.get(MemoryId::new(1)), // ACL memory
mm.get(MemoryId::new(2)), // shared keys memory
mm.get(MemoryId::new(3)), // encrypted data memory
));
});
4. vetkd skill: Missing getrandom dependency for WASM
Building ic-vetkeys for wasm32-unknown-unknown fails without:
getrandom = { version = "0.2", features = ["custom"] }
The error is:
error: the wasm*-unknown-unknown targets are not supported by default,
you may need to enable the "js" feature.
This is a hard build failure that every Rust vetKeys project will hit. Should be in the skill's Cargo.toml example.
5. stable-memory skill: Missing into_bytes in Storable example
The Rust Storable trait in ic-stable-structures v0.7 requires into_bytes(self) -> Vec<u8> in addition to to_bytes(&self) -> Cow<[u8]>. The skill's example omits into_bytes, causing a compilation error:
error[E0046]: not all trait items implemented, missing: `into_bytes`
6. icp-cli skill: No documentation for custom gateway port
When running multiple ICP projects simultaneously, they all default to port 8000 and conflict. The gateway.port configuration option exists (confirmed via icp-yaml-schema.json) but is not documented in any skill:
networks:
- name: local
mode: managed
gateway:
port: 8001
7. icp-cli skill: candid-extractor workflow not documented
The workflow for generating .did files from Rust canisters using export_candid!() is not covered by any skill. The process:
- Add
ic_cdk::export_candid!(); to lib.rs
- Build:
cargo build --target wasm32-unknown-unknown --release
- Extract:
candid-extractor target/wasm32-unknown-unknown/release/<name>.wasm > <name>.did
- Use with
@icp-sdk/bindgen for TypeScript bindings
candid-extractor must be installed separately (cargo install candid-extractor). Without this workflow documented, developers have no clear path from export_candid!() to TypeScript bindings.
Suggested new skills
encrypted-maps
A skill covering ic-vetkeys's EncryptedMaps — the primary abstraction for building encrypted storage applications. Should cover:
- Init with 4 memory regions
- Type system:
KeyId, MapKey, EncryptedMapValue, ByteBuf
- CRUD operations on encrypted values
- Access control: grant, revoke, list shared users
- Frontend pattern: deriving key material for another user's map (shared data decryption)
- Complete working example
candid-generation
A skill covering .did file generation and TypeScript binding creation:
export_candid!() macro usage
candid-extractor tool
@icp-sdk/bindgen Vite plugin configuration
- The full pipeline: Rust → WASM → .did → TypeScript bindings → frontend actor
Version matrix
All working together as of March 2026:
| Dependency |
Version |
ic-cdk |
0.19 |
ic-stable-structures |
0.7 |
ic-vetkeys |
0.6 |
candid |
0.10 |
getrandom |
0.2 (features = ["custom"]) |
@icp-sdk/core |
5.1.0 |
@icp-sdk/bindgen |
0.2.3 |
@dfinity/vetkeys |
0.4.0 |
| Rust |
1.93.0 |
| Node.js |
24.8.0 |
Report from building a production application using
ic-vetkeys(EncryptedMaps, KeyManager) with a React frontend. Covers skills used, issues encountered, and suggestions for new skills.Issues already reported in #96 (Illegal invocation fetch fix, SDK version/import issues, local II deployment, ic_env cookie issues) are excluded here — this report focuses on new findings only.
1.
vetkdskill: Rust API mismatchesThe skill's Rust code shows types imported from
ic_vetkeys::typesthat actually live inic_cdk::management_canister:The enum variant name is also wrong:
These cause immediate compilation failures.
2.
vetkdskill: TypeScript API doesn't match@dfinity/vetkeysv0.4.0The skill's frontend examples use an API that doesn't exist in the current npm package:
@dfinity/vetkeysv0.4.0 APITransportSecretKey.fromSeed(seed)TransportSecretKey.random()tsk.publicKey()tsk.publicKeyBytes()vetKey.toDerivedKeyMaterial()vetKey.asDerivedKeyMaterial()(returnsPromise)crypto.subtle.importKey("raw", material.data.slice(0,32), ...)keyMaterial.deriveAesGcmCryptoKey(domainSep)The
DerivedKeyMaterialclass has a much richer API than documented —encryptMessage(),decryptMessage(), andderiveAesGcmCryptoKey()handle IV generation and key derivation internally. The skill's manualcrypto.subtleapproach works but is unnecessary and error-prone.3.
vetkdskill:EncryptedMapsnot documentedThe
ic-vetkeyscrate'sEncryptedMaps<T: AccessControl>is the primary abstraction most developers will want — it wrapsKeyManager+StableBTreeMapinto a single struct with integrated encrypted storage and access control. It's not mentioned in the skill at all.Key API details discovered by reading crate source:
EncryptedMaps::init()takes 4 memory regions (not 3 likeKeyManager): config, ACL, shared keys, and data storageKeyId = (Principal, Blob<32>)— owner principal + 32-byte map nameMapKey = Blob<32>— entries within a map use 32-byte keysEncryptedMapValue = ByteBuf— the crate's ownByteBuftype (notVec<u8>; hasFrom/Intoconversions)ReadWriteManagerightsset_user_rights()/remove_user()for granting/revoking accessget_encrypted_values_for_map()checks ACL before returning ciphertextget_encrypted_vetkey()also checks ACL — access control and encryption are coupled (two independent locks)get_accessible_shared_map_names()for discovering what's been shared with youget_all_accessible_encrypted_maps()for bulk retrievalExample init (not in any skill):
4.
vetkdskill: Missinggetrandomdependency for WASMBuilding
ic-vetkeysforwasm32-unknown-unknownfails without:The error is:
This is a hard build failure that every Rust vetKeys project will hit. Should be in the skill's Cargo.toml example.
5.
stable-memoryskill: Missinginto_bytesin Storable exampleThe Rust
Storabletrait inic-stable-structuresv0.7 requiresinto_bytes(self) -> Vec<u8>in addition toto_bytes(&self) -> Cow<[u8]>. The skill's example omitsinto_bytes, causing a compilation error:6.
icp-cliskill: No documentation for custom gateway portWhen running multiple ICP projects simultaneously, they all default to port 8000 and conflict. The
gateway.portconfiguration option exists (confirmed viaicp-yaml-schema.json) but is not documented in any skill:7.
icp-cliskill:candid-extractorworkflow not documentedThe workflow for generating
.didfiles from Rust canisters usingexport_candid!()is not covered by any skill. The process:ic_cdk::export_candid!();to lib.rscargo build --target wasm32-unknown-unknown --releasecandid-extractor target/wasm32-unknown-unknown/release/<name>.wasm > <name>.did@icp-sdk/bindgenfor TypeScript bindingscandid-extractormust be installed separately (cargo install candid-extractor). Without this workflow documented, developers have no clear path fromexport_candid!()to TypeScript bindings.Suggested new skills
encrypted-mapsA skill covering
ic-vetkeys'sEncryptedMaps— the primary abstraction for building encrypted storage applications. Should cover:KeyId,MapKey,EncryptedMapValue,ByteBufcandid-generationA skill covering
.didfile generation and TypeScript binding creation:export_candid!()macro usagecandid-extractortool@icp-sdk/bindgenVite plugin configurationVersion matrix
All working together as of March 2026:
ic-cdkic-stable-structuresic-vetkeyscandidgetrandom@icp-sdk/core@icp-sdk/bindgen@dfinity/vetkeys