From 8f2606570fd7cdac5b898ba07f33d9e906c57b9d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Fri, 29 May 2026 14:21:54 +0200 Subject: [PATCH 01/30] chore: migrate encrypted_notes_app_vetkd to icp CLI and @icp-sdk/vetkeys Co-Authored-By: Claude Sonnet 4.6 --- ...keys-encrypted-notes-app-vetkd-example.yml | 103 +++++++++++++++ .../README.md | 45 ++++--- .../frontend}/index.html | 10 +- .../frontend/package.json | 39 ++++++ .../frontend/postcss.config.mjs | 6 + .../frontend/public/.ic-assets.json5 | 0 .../frontend/public/favicon.png | Bin ...wered-by-crypto_label-stripe-dark-text.png | Bin ...ered-by-crypto_label-stripe-white-text.png | Bin ...owered-by-crypto_transparent-dark-text.png | Bin ...wered-by-crypto_transparent-white-text.png | Bin .../frontend/scripts/gen_bindings.sh | 9 ++ .../frontend/src/App.svelte | 0 .../frontend/src/components/Disclaimer.svelte | 0 .../src/components/DisclaimerCopy.svelte | 0 .../frontend/src/components/EditNote.svelte | 0 .../frontend/src/components/Header.svelte | 0 .../frontend/src/components/Hero.svelte | 0 .../src/components/LayoutAuthenticated.svelte | 0 .../frontend/src/components/NewNote.svelte | 0 .../frontend/src/components/Note.svelte | 0 .../frontend/src/components/NoteEditor.svelte | 0 .../frontend/src/components/Notes.svelte | 0 .../src/components/Notifications.svelte | 0 .../src/components/SharingEditor.svelte | 0 .../src/components/SidebarLayout.svelte | 0 .../frontend/src/components/Spinner.svelte | 0 .../frontend/src/components/TagEditor.svelte | 0 .../frontend/src/global.d.ts | 0 .../frontend/src/lib/actor.ts | 20 +++ .../frontend/src/lib/crypto.ts | 5 +- .../frontend/src/lib/enums.ts | 0 .../frontend/src/lib/note.ts | 4 +- .../frontend/src/lib/sleep.ts | 0 .../frontend/src/main.ts | 0 .../frontend/src/store/auth.ts | 79 ++++-------- .../frontend/src/store/draft.ts | 0 .../frontend/src/store/notes.ts | 0 .../frontend/src/store/notifications.ts | 0 .../frontend/svelte.config.js | 5 + .../frontend/tailwind.config.mjs | 11 ++ .../frontend/tsconfig.json | 5 + .../frontend/vite.config.ts | 44 +++++++ .../motoko/backend/main.mo | 0 .../motoko/backend/utils/Hex.mo | 0 .../motoko/frontend | 0 .../encrypted_notes_app_vetkd/motoko/icp.yaml | 24 ++++ .../motoko/mops.toml | 2 + .../rust/Cargo.toml | 0 .../rust/backend/Cargo.toml | 0 .../rust/backend/src/encrypted_notes_rust.did | 0 .../rust/backend/src/lib.rs | 0 .../rust/frontend | 0 .../encrypted_notes_app_vetkd/rust/icp.yaml | 25 ++++ .../rust/rust-toolchain.toml | 0 .../security-checklist.md | 0 .../frontend/package.json | 81 ------------ .../frontend/rollup.config.js | 121 ------------------ .../frontend/scripts/gen_bindings.sh | 9 -- .../frontend/src/lib/actor.ts | 54 -------- .../frontend/tailwind.config.js | 10 -- .../frontend/tsconfig.json | 5 - .../motoko/dfx.json | 39 ------ .../encrypted_notes_dapp_vetkd/rust/dfx.json | 39 ------ 64 files changed, 346 insertions(+), 448 deletions(-) create mode 100644 .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/README.md (54%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd/frontend/public => encrypted_notes_app_vetkd/frontend}/index.html (67%) create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/package.json create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/postcss.config.mjs rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/.ic-assets.json5 (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/favicon.png (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-dark-text.png (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-white-text.png (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/img/ic-badge-powered-by-crypto_transparent-dark-text.png (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/public/img/ic-badge-powered-by-crypto_transparent-white-text.png (100%) create mode 100755 rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/App.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Disclaimer.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/DisclaimerCopy.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/EditNote.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Header.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Hero.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/LayoutAuthenticated.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/NewNote.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Note.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/NoteEditor.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Notes.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Notifications.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/SharingEditor.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/SidebarLayout.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/Spinner.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/components/TagEditor.svelte (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/global.d.ts (100%) create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/lib/crypto.ts (93%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/lib/enums.ts (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/lib/note.ts (94%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/lib/sleep.ts (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/main.ts (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/store/auth.ts (53%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/store/draft.ts (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/store/notes.ts (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/frontend/src/store/notifications.ts (100%) create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/svelte.config.js create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/tailwind.config.mjs create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/tsconfig.json create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/frontend/vite.config.ts rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/motoko/backend/main.mo (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/motoko/backend/utils/Hex.mo (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/motoko/frontend (100%) create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/Cargo.toml (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/backend/Cargo.toml (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/backend/src/encrypted_notes_rust.did (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/backend/src/lib.rs (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/frontend (100%) create mode 100644 rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/rust/rust-toolchain.toml (100%) rename rust/vetkeys/{encrypted_notes_dapp_vetkd => encrypted_notes_app_vetkd}/security-checklist.md (100%) delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/package.json delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/rollup.config.js delete mode 100755 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/scripts/gen_bindings.sh delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/actor.ts delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tailwind.config.js delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tsconfig.json delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/dfx.json delete mode 100644 rust/vetkeys/encrypted_notes_dapp_vetkd/rust/dfx.json diff --git a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml new file mode 100644 index 000000000..051d203d9 --- /dev/null +++ b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml @@ -0,0 +1,103 @@ +name: rust-vetkeys-encrypted-notes-app-vetkd +on: + push: + branches: + - master + pull_request: + paths: + - rust/vetkeys/encrypted_notes_app_vetkd/** + - .github/workflows/provision-darwin.sh + - .github/workflows/provision-linux.sh + - .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml + - .ic-commit +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + rust-vetkeys-encrypted-notes-app-vetkd-rust-darwin: + runs-on: macos-15 + steps: + - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + - name: Provision Darwin + run: bash .github/workflows/provision-darwin.sh + - name: Pre-download network launcher + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash .github/workflows/pre-download-launcher.sh + - name: Deploy (Rust backend) + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eExuo pipefail + pushd rust/vetkeys/encrypted_notes_app_vetkd/rust + icp network start -d && icp deploy + popd + + rust-vetkeys-encrypted-notes-app-vetkd-motoko-darwin: + runs-on: macos-15 + steps: + - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + - name: Provision Darwin + run: bash .github/workflows/provision-darwin.sh + - name: Pre-download network launcher + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash .github/workflows/pre-download-launcher.sh + - name: Deploy (Motoko backend) + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eExuo pipefail + pushd rust/vetkeys/encrypted_notes_app_vetkd/motoko + icp network start -d && icp deploy + popd + + rust-vetkeys-encrypted-notes-app-vetkd-rust-linux: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + - name: Provision Linux + run: bash .github/workflows/provision-linux.sh + - name: Pre-download network launcher + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash .github/workflows/pre-download-launcher.sh + - name: Deploy (Rust backend) + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eExuo pipefail + pushd rust/vetkeys/encrypted_notes_app_vetkd/rust + icp network start -d && icp deploy + popd + + rust-vetkeys-encrypted-notes-app-vetkd-motoko-linux: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + - name: Provision Linux + run: bash .github/workflows/provision-linux.sh + - name: Pre-download network launcher + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: bash .github/workflows/pre-download-launcher.sh + - name: Deploy (Motoko backend) + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -eExuo pipefail + pushd rust/vetkeys/encrypted_notes_app_vetkd/motoko + icp network start -d && icp deploy + popd diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/README.md b/rust/vetkeys/encrypted_notes_app_vetkd/README.md similarity index 54% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/README.md rename to rust/vetkeys/encrypted_notes_app_vetkd/README.md index 01d63e3ea..ab4cd89ef 100644 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/README.md +++ b/rust/vetkeys/encrypted_notes_app_vetkd/README.md @@ -1,58 +1,57 @@ # Encrypted notes: vetKD + -Encrypted notes is an example dapp for authoring and storing confidential information on the Internet Computer (ICP) in the form of short pieces of text. Users can create and access their notes via any number of automatically synchronized devices authenticated via Internet Identity (II). Notes are stored confidentially using vetKeys. The end-to-end encryption is performed by the dapp’s frontend. +Encrypted notes is an example dapp for authoring and storing confidential information on the Internet Computer (ICP) in the form of short pieces of text. Users can create and access their notes via any number of automatically synchronized devices authenticated via Internet Identity (II). Notes are stored confidentially using vetKeys. The end-to-end encryption is performed by the dapp's frontend. In particular, the notes are encrypted with an AES key that is derived (directly in the browser) from a note-ID-specific vetKey obtained from the backend canister (in encrypted form, using an ephemeral transport key), which itself obtains it from the vetKD system API. This way, there is no need for any device management in the dapp, plus sharing of notes becomes possible. -The vetKey used to encrypt and decrypt a note is note-ID-specific (and not, for example, principal-specific) to enable the sharing of notes between users. The derived AES keys are stored as non-extractable CryptoKeys in an IndexedDB in the browser for efficiency so that their respective vetKey only has to be fetched from the server once. To improve the security even further, the vetKeys' derivation information could be adapted to include a (numeric) epoch that advances each time the list of users with which the note is shared is changed. +The vetKey used to encrypt and decrypt a note is note-ID-specific (and not, for example, principal-specific) to enable the sharing of notes between users. The derived AES keys are stored as non-extractable CryptoKeys in an IndexedDB in the browser for efficiency so that their respective vetKey only has to be fetched from the server once. ## Prerequisites -This example requires an installation of: - -- [x] Install the [IC SDK](https://internetcomputer.org/docs/current/developer-docs/setup/install/index.mdx). +- [x] Install the [ICP CLI](https://cli.internetcomputer.org). - [x] Install [npm](https://www.npmjs.com/package/npm). -### (Optionally) Choose a Different Master Key - -This example uses `test_key_1` by default. To use a different [available master key](https://internetcomputer.org/docs/building-apps/network-features/vetkeys/api#available-master-keys), change the `"init_arg": "(\"test_key_1\")"` line in `dfx.json` to the desired key before running `dfx deploy` in the next step. - ## Deploy the Canisters Locally -If you want to deploy this project locally with a Motoko backend, then run: +To deploy with the Motoko backend, run from the `motoko` folder: ```bash -dfx start --background && dfx deploy +icp network start -d && icp deploy ``` -from the `motoko` folder. -To use the Rust backend instead of Motoko, run the same command in the rust folder. +To deploy with the Rust backend instead, run the same command from the `rust` folder. + +## Running the Frontend in Development Mode + +After deploying, run from the `frontend` folder: +```bash +# For Motoko backend: +npm run dev:motoko + +# For Rust backend: +npm run dev:rust +``` ## Example Components ### Backend -The backend consists of a canister that stores encrypted notes. It is automatically deployed with `dfx deploy`. +The backend consists of a canister that stores encrypted notes. It is automatically deployed with `icp deploy`. ### Frontend The frontend is a **Svelte** application providing a user-friendly interface for managing encrypted notes. -To run the frontend in development mode with hot reloading (after running `dfx deploy`): - -```bash -npm run dev -``` - ## Limitations This example dapp does not implement key rotation, which is strongly recommended in a production environment. -Key rotation involves periodically changing encryption keys and re-encrypting data to enhance security. -In a production dapp, key rotation would be useful to limit the impact of potential key compromise if a malicious party gains access to a key, or to limit access when users are added or removed from note sharing. ## Troubleshooting -If you run into issues, clearing all the application-specific IndexedDBs in the browser (which are used to store Internet Identity information and the derived non-extractable AES keys) might help fix the issue. For example in Chrome, go to Inspect → Application → Local Storage → `http://localhost:3000/` → Clear All, and then reload. +If you run into issues, clearing all the application-specific IndexedDBs in the browser might help. For example in Chrome, go to Inspect → Application → Local Storage → Clear All, and then reload. diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/index.html b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/index.html similarity index 67% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/index.html rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/index.html index abc12ca3b..a494e91ea 100644 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/index.html +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/index.html @@ -3,14 +3,10 @@ - Encrypted Notes - - - - - - + + + diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/package.json b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/package.json new file mode 100644 index 000000000..0bcb2ba2e --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/package.json @@ -0,0 +1,39 @@ +{ + "name": "encrypted-notes-frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "printf '\\nNo backend specified. Use one of:\\n\\n npm run dev:motoko\\n npm run dev:rust\\n\\n' && exit 1", + "dev:motoko": "npm run build:bindings && BACKEND=motoko vite", + "dev:rust": "npm run build:bindings && BACKEND=rust vite", + "build": "npm run build:bindings && vite build", + "build:bindings": "cd scripts && ./gen_bindings.sh", + "preview": "vite preview", + "check": "svelte-check --tsconfig ./tsconfig.json" + }, + "devDependencies": { + "@sveltejs/vite-plugin-svelte": "^3.0.2", + "@tsconfig/svelte": "^5.0.4", + "@types/node": "^24.0.10", + "autoprefixer": "^10.4.2", + "postcss": "^8.4.31", + "svelte": "^4.2.19", + "svelte-check": "^3.8.6", + "tailwindcss": "^3.0.17", + "tslib": "^2.8.1", + "typescript": "~5.7.2", + "vite": "^6.4.1" + }, + "dependencies": { + "@icp-sdk/auth": "^7.1.0", + "@icp-sdk/core": "^5.4.0", + "@icp-sdk/vetkeys": "^0.5.0-beta.0", + "daisyui": "^1.25.4", + "idb-keyval": "6.2.1", + "isomorphic-dompurify": "^2.25.0", + "svelte-icons": "^2.1.0", + "svelte-router-spa": "^6.0.3", + "typewriter-editor": "^0.6.45" + } +} diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/postcss.config.mjs b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/postcss.config.mjs new file mode 100644 index 000000000..8409575db --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/postcss.config.mjs @@ -0,0 +1,6 @@ +import tailwindcss from 'tailwindcss'; +import autoprefixer from 'autoprefixer'; + +export default { + plugins: [tailwindcss(), autoprefixer()], +}; diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/.ic-assets.json5 b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/.ic-assets.json5 similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/.ic-assets.json5 rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/.ic-assets.json5 diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/favicon.png b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/favicon.png similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/favicon.png rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/favicon.png diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-dark-text.png b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-dark-text.png similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-dark-text.png rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-dark-text.png diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-white-text.png b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-white-text.png similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-white-text.png rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_label-stripe-white-text.png diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-dark-text.png b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-dark-text.png similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-dark-text.png rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-dark-text.png diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-white-text.png b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-white-text.png similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-white-text.png rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/public/img/ic-badge-powered-by-crypto_transparent-white-text.png diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh new file mode 100755 index 000000000..521c6cece --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# Bindings are always generated from the Rust backend since both backends +# expose the same Candid interface. + +cd ../.. +rm -rf frontend/src/declarations/encrypted_notes +mkdir -p frontend/src/declarations/encrypted_notes +npx @icp-sdk/bindgen --did-file rust/backend/src/encrypted_notes_rust.did --out-dir frontend/src/declarations/encrypted_notes --declarations-flat --force diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/App.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/App.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/App.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/App.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Disclaimer.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Disclaimer.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Disclaimer.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Disclaimer.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/DisclaimerCopy.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/DisclaimerCopy.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/DisclaimerCopy.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/DisclaimerCopy.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/EditNote.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/EditNote.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Header.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Header.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Header.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Header.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Hero.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Hero.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Hero.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Hero.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/LayoutAuthenticated.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/LayoutAuthenticated.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/LayoutAuthenticated.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/LayoutAuthenticated.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/NewNote.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/NewNote.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Note.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Note.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Note.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Note.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/NoteEditor.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NoteEditor.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/NoteEditor.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NoteEditor.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Notes.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Notes.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Notes.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Notes.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Notifications.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Notifications.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Notifications.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Notifications.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/SharingEditor.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SharingEditor.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/SharingEditor.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SharingEditor.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/SidebarLayout.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/SidebarLayout.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Spinner.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Spinner.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/Spinner.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/Spinner.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/TagEditor.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/TagEditor.svelte similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/components/TagEditor.svelte rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/TagEditor.svelte diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/global.d.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/global.d.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/global.d.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/global.d.ts diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts new file mode 100644 index 000000000..9e0e5bccd --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -0,0 +1,20 @@ +import { Actor, HttpAgent, type ActorSubclass } from "@icp-sdk/core/agent"; +import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; +import { idlFactory, type _SERVICE } from "../declarations/encrypted_notes/backend.did"; + +export type BackendActor = ActorSubclass<_SERVICE>; + +const canisterEnv = safeGetCanisterEnv<{ + "PUBLIC_CANISTER_ID:encrypted_notes": string; + ic_root_key?: string; +}>(); + +export async function createActor(options?: { identity?: any }): Promise { + const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:encrypted_notes"]; + const agent = await HttpAgent.create({ + identity: options?.identity, + host: window.location.origin, + ...(canisterEnv?.ic_root_key ? { rootKey: canisterEnv.ic_root_key } : {}), + }); + return Actor.createActor(idlFactory, { agent, canisterId }); +} diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/crypto.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/crypto.ts similarity index 93% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/crypto.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/crypto.ts index cb96c4b83..820c1c2e9 100644 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/crypto.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/crypto.ts @@ -1,9 +1,6 @@ import type { BackendActor } from './actor'; import { get, set } from 'idb-keyval'; - -// Usage of the imported bindings only works if the respective .wasm was loaded, which is done in main.ts. -// See also https://github.com/rollup/plugins/tree/master/packages/wasm#using-with-wasm-bindgen-and-wasm-pack -import * as vetkd from "@dfinity/vetkeys"; +import * as vetkd from "@icp-sdk/vetkeys"; export class CryptoService { constructor(private actor: BackendActor) { diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/enums.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/enums.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/enums.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/enums.ts diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/note.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/note.ts similarity index 94% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/note.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/note.ts index 50a4d5959..10d6598c4 100644 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/note.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/note.ts @@ -1,6 +1,6 @@ -import type { EncryptedNote } from '../lib/backend'; +import type { EncryptedNote } from '../declarations/encrypted_notes/backend.did'; import type { CryptoService } from './crypto'; -import type { Principal } from '@dfinity/principal'; +import type { Principal } from '@icp-sdk/core/principal'; export interface NoteModel { id: bigint; diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/sleep.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/sleep.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/sleep.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/sleep.ts diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/main.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/main.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/main.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/main.ts diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/auth.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts similarity index 53% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/auth.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts index 04c7540db..98c67b80b 100644 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/auth.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts @@ -1,54 +1,29 @@ import { get, writable } from "svelte/store"; -import { BackendActor, createActor } from "../lib/actor"; -import { AuthClient } from "@dfinity/auth-client"; +import { type BackendActor, createActor } from "../lib/actor"; +import { AuthClient } from "@icp-sdk/auth/client"; import { CryptoService } from "../lib/crypto"; -import { addNotification, showError } from "./notifications"; -import { sleep } from "../lib/sleep"; -import type { JsonnableDelegationChain } from "@dfinity/identity/lib/cjs/identity/delegation"; +import { showError } from "./notifications"; import { navigateTo } from "svelte-router-spa"; export type AuthState = - | { - state: "initializing-auth"; - } - | { - state: "anonymous"; - actor: BackendActor; - client: AuthClient; - } - | { - state: "initializing-crypto"; - actor: BackendActor; - client: AuthClient; - } - | { - state: "synchronizing"; - actor: BackendActor; - client: AuthClient; - } - | { - state: "initialized"; - actor: BackendActor; - client: AuthClient; - crypto: CryptoService; - } - | { - state: "error"; - error: string; - }; + | { state: "initializing-auth" } + | { state: "anonymous"; actor: BackendActor; client: AuthClient } + | { state: "initializing-crypto"; actor: BackendActor; client: AuthClient } + | { state: "synchronizing"; actor: BackendActor; client: AuthClient } + | { state: "initialized"; actor: BackendActor; client: AuthClient; crypto: CryptoService } + | { state: "error"; error: string }; -export const auth = writable({ - state: "initializing-auth", -}); +export const auth = writable({ state: "initializing-auth" }); async function initAuth() { const client = await AuthClient.create(); if (await client.isAuthenticated()) { authenticate(client); } else { + const actor = await createActor(); auth.update(() => ({ state: "anonymous", - actor: createActor(), + actor, client, })); } @@ -63,9 +38,10 @@ export function login() { currentAuth.client.login({ maxTimeToLive: BigInt(1800) * BigInt(1_000_000_000), identityProvider: - process.env.DFX_NETWORK === "ic" - ? "https://identity.ic0.app/#authorize" - : `http://rdmx6-jaaaa-aaaaa-aaadq-cai.localhost:8000/#authorize`, + window.location.hostname === "localhost" || + window.location.hostname.endsWith(".localhost") + ? `http://id.ai.localhost:8000/#authorize` + : "https://identity.ic0.app/#authorize", onSuccess: () => authenticate(currentAuth.client), }); } @@ -76,9 +52,10 @@ export async function logout() { if (currentAuth.state === "initialized") { await currentAuth.client.logout(); + const actor = await createActor(); auth.update(() => ({ state: "anonymous", - actor: createActor(), + actor, client: currentAuth.client, })); navigateTo("/"); @@ -89,11 +66,7 @@ export async function authenticate(client: AuthClient) { handleSessionTimeout(); try { - const actor = createActor({ - agentOptions: { - identity: client.getIdentity(), - }, - }); + const actor = await createActor({ identity: client.getIdentity() }); auth.update(() => ({ state: "initializing-crypto", @@ -109,7 +82,7 @@ export async function authenticate(client: AuthClient) { client, crypto: cryptoService, })); - } catch (e) { + } catch (e: any) { auth.update(() => ({ state: "error", error: e.message || "An error occurred", @@ -117,18 +90,16 @@ export async function authenticate(client: AuthClient) { } } -// set a timer when the II session will expire and log the user out function handleSessionTimeout() { - // upon login the localstorage items may not be set, wait for next tick setTimeout(() => { try { - const delegation = JSON.parse( - window.localStorage.getItem("ic-delegation") - ) as JsonnableDelegationChain; + const delegation = JSON.parse(window.localStorage.getItem("ic-delegation") ?? "null") as { + delegations: Array<{ delegation: { expiration: string } }>; + } | null; + if (!delegation) return; const expirationTimeMs = - Number.parseInt(delegation.delegations[0].delegation.expiration, 16) / - 1000000; + Number.parseInt(delegation.delegations[0].delegation.expiration, 16) / 1000000; setTimeout(() => { logout(); diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/draft.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/draft.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/draft.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/draft.ts diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/notes.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/notes.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/notes.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/notes.ts diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/notifications.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/notifications.ts similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/store/notifications.ts rename to rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/notifications.ts diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/svelte.config.js b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/svelte.config.js new file mode 100644 index 000000000..8abe4369b --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/svelte.config.js @@ -0,0 +1,5 @@ +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' + +export default { + preprocess: vitePreprocess(), +} diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tailwind.config.mjs b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tailwind.config.mjs new file mode 100644 index 000000000..1812a8a38 --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tailwind.config.mjs @@ -0,0 +1,11 @@ +export default { + content: [ + './index.html', + './src/**/*.svelte', + './src/**/*.ts', + ], + theme: { + extend: {}, + }, + plugins: [], +}; diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tsconfig.json b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tsconfig.json new file mode 100644 index 000000000..4365ffeb0 --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "include": ["src/**/*", "index.html"], + "exclude": ["node_modules/*", "public/*", "src/declarations/**/*"] +} diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/vite.config.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/vite.config.ts new file mode 100644 index 000000000..3d6221a96 --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/vite.config.ts @@ -0,0 +1,44 @@ +import { defineConfig } from "vite"; +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import { execSync } from "child_process"; + +const environment = process.env.ICP_ENVIRONMENT || "local"; +const CANISTER_NAMES = ["encrypted_notes"]; + +function getDevServerConfig() { + const backend = process.env.BACKEND; + if (!backend) { + throw new Error("BACKEND env var is required. Use `npm run dev:motoko` or `npm run dev:rust`."); + } + const networkStatus = JSON.parse( + execSync(`icp network status -e ${environment} --json --project-root-override ../${backend}`, { encoding: "utf-8" }), + ); + const canisterParams = CANISTER_NAMES.map((name) => { + const id = execSync( + `icp canister status ${name} -e ${environment} --id-only --project-root-override ../${backend}`, + { encoding: "utf-8", stdio: "pipe" }, + ).trim(); + return `PUBLIC_CANISTER_ID:${name}=${id}`; + }).join("&"); + return { + headers: { + "Set-Cookie": `ic_env=${encodeURIComponent(`${canisterParams}&ic_root_key=${networkStatus.root_key}`)}; SameSite=Lax;`, + }, + proxy: { "/api": { target: networkStatus.api_url, changeOrigin: true } }, + hmr: false, + }; +} + +export default defineConfig(({ command }) => ({ + plugins: [svelte()], + build: { + sourcemap: true, + rollupOptions: { + output: { + inlineDynamicImports: true, + }, + }, + }, + root: "./", + ...(command === "serve" ? { server: getDevServerConfig() } : {}), +})); diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/backend/main.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/backend/main.mo rename to rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/backend/utils/Hex.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/backend/utils/Hex.mo rename to rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/frontend b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/frontend similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/frontend rename to rust/vetkeys/encrypted_notes_app_vetkd/motoko/frontend diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml new file mode 100644 index 000000000..3378b091a --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml @@ -0,0 +1,24 @@ +canisters: + - name: encrypted_notes + recipe: + type: "@dfinity/motoko@v4.1.0" + configuration: + main: backend/main.mo + init_args: + type: text + value: "(\"test_key_1\")" + + - name: www + recipe: + type: "@dfinity/asset-canister@v2.1.0" + configuration: + dir: dist + build: + - npm ci --prefix ../frontend + - npm run build --prefix ../frontend + - rm -rf dist && mv ../frontend/dist dist + +networks: + - name: local + mode: managed + ii: true diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml new file mode 100644 index 000000000..b227db842 --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml @@ -0,0 +1,2 @@ +[toolchain] +moc = "1.5.1" diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/Cargo.toml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/Cargo.toml similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/Cargo.toml rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/Cargo.toml diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/Cargo.toml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/Cargo.toml rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/src/encrypted_notes_rust.did b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/encrypted_notes_rust.did similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/src/encrypted_notes_rust.did rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/encrypted_notes_rust.did diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/src/lib.rs b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/backend/src/lib.rs rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/frontend b/rust/vetkeys/encrypted_notes_app_vetkd/rust/frontend similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/frontend rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/frontend diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml new file mode 100644 index 000000000..6d868c9f1 --- /dev/null +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml @@ -0,0 +1,25 @@ +canisters: + - name: encrypted_notes + recipe: + type: "@dfinity/rust@v3.2.0" + configuration: + package: encrypted_notes_backend + candid: backend/src/encrypted_notes_rust.did + init_args: + type: text + value: "(\"test_key_1\")" + + - name: www + recipe: + type: "@dfinity/asset-canister@v2.1.0" + configuration: + dir: dist + build: + - npm ci --prefix ../frontend + - npm run build --prefix ../frontend + - rm -rf dist && mv ../frontend/dist dist + +networks: + - name: local + mode: managed + ii: true diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/rust-toolchain.toml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/rust/rust-toolchain.toml rename to rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/security-checklist.md b/rust/vetkeys/encrypted_notes_app_vetkd/security-checklist.md similarity index 100% rename from rust/vetkeys/encrypted_notes_dapp_vetkd/security-checklist.md rename to rust/vetkeys/encrypted_notes_app_vetkd/security-checklist.md diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/package.json b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/package.json deleted file mode 100644 index 2a36dba9e..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/package.json +++ /dev/null @@ -1,81 +0,0 @@ -{ - "name": "encrypted-notes-dapp", - "version": "0.2.0", - "keywords": [ - "Internet Computer", - "Motoko", - "Svelte", - "Canister", - "Rust" - ], - "scripts": { - "build": "npm run build:bindings && rollup -c --bundleConfigAsCjs", - "build:bindings": "cd scripts && ./gen_bindings.sh", - "dev": "npm run build:bindings && rollup -c --bundleConfigAsCjs -w", - "start": "sirv public --single", - "test": "jest src", - "test:watch": "npm run test -- --watch", - "check": "svelte-check --tsconfig ./tsconfig.json" - }, - "devDependencies": { - "@babel/preset-env": "^7.16.8", - "@rollup/plugin-commonjs": "^25.0.0", - "@rollup/plugin-json": "^6.0.0", - "@rollup/plugin-node-resolve": "^15.0.2", - "@rollup/plugin-terser": "^1.0.0", - "@rollup/plugin-typescript": "^12.1.2", - "@tailwindcss/line-clamp": "^0.3.1", - "@testing-library/jest-dom": "^5.16.1", - "@testing-library/svelte": "^3.0.3", - "@tsconfig/svelte": "^2.0.0", - "autoprefixer": "^10.4.2", - "babel-jest": "^27.4.6", - "daisyui": "^1.25.4", - "idb-keyval": "6.2.1", - "jest": "^30.2.0", - "postcss": "^8.4.31", - "rollup": "^3.30.0", - "rollup-plugin-css-only": "^4.3.0", - "rollup-plugin-dotenv": "^0.5.1", - "rollup-plugin-inject": "^3.0.2", - "rollup-plugin-inject-process-env": "^1.3.1", - "rollup-plugin-livereload": "^2.0.0", - "rollup-plugin-polyfill-node": "^0.12.0", - "rollup-plugin-svelte": "^7.2.2", - "svelte": "^3.59.1", - "svelte-check": "^3.3.2", - "svelte-jester": "^2.3.2", - "svelte-preprocess": "^5.0.3", - "tailwindcss": "^3.0.17", - "tslib": "^2.0.0", - "typescript": "^4.0.0" - }, - "dependencies": { - "@dfinity/agent": "^2.1.3", - "@dfinity/auth-client": "^2.1.3", - "@dfinity/candid": "^2.1.3", - "@dfinity/identity": "^2.1.3", - "@dfinity/principal": "^2.1.3", - "@dfinity/vetkeys": "^0.3.0", - "isomorphic-dompurify": "^2.25.0", - "sirv-cli": "^1.0.0", - "svelte-icons": "^2.1.0", - "svelte-router-spa": "^6.0.3", - "typewriter-editor": "^0.6.45" - }, - "jest": { - "transform": { - "^.+\\.js$": "babel-jest", - "^.+\\.svelte$": "svelte-jester" - }, - "moduleFileExtensions": [ - "js", - "svelte" - ], - "setupFilesAfterEnv": [ - "@testing-library/jest-dom/extend-expect", - "./jest-env.js" - ], - "testEnvironment": "jsdom" - } -} diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/rollup.config.js b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/rollup.config.js deleted file mode 100644 index 535cf2306..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/rollup.config.js +++ /dev/null @@ -1,121 +0,0 @@ -import svelte from "rollup-plugin-svelte"; -import commonjs from "@rollup/plugin-commonjs"; -import resolve from "@rollup/plugin-node-resolve"; -import livereload from "rollup-plugin-livereload"; -import terser from "@rollup/plugin-terser"; -import sveltePreprocess from "svelte-preprocess"; -import typescript from "@rollup/plugin-typescript"; -import css from "rollup-plugin-css-only"; -import json from "@rollup/plugin-json"; -import injectProcessEnv from "rollup-plugin-inject-process-env"; - -const production = !process.env.ROLLUP_WATCH; - -function serve(exposeHost) { - let server; - - function toExit() { - if (server) server.kill(0); - } - - return { - writeBundle() { - if (server) return; - server = require("child_process").spawn( - "npm", - exposeHost - ? ["run", "start-expose", "--", "--dev"] - : ["run", "start", "--", "--dev"], - { - stdio: ["ignore", "inherit", "inherit"], - shell: true, - } - ); - - process.on("SIGTERM", toExit); - process.on("exit", toExit); - }, - }; -} - -export default (config) => { - const exposeHost = !!config.configExpose; - - return { - input: "src/main.ts", - output: { - sourcemap: true, - name: "app", - format: "iife", - - file: "public/build/main.js", - inlineDynamicImports: true, - }, - plugins: [ - svelte({ - preprocess: sveltePreprocess({ - sourceMap: !production, - postcss: { - plugins: [require("tailwindcss")(), require("autoprefixer")()], - }, - }), - compilerOptions: { - // enable run-time checks when not in production - dev: !production, - }, - }), - // we'll extract any component CSS out into - // a separate file - better for performance - css({ output: "bundle.css" }), - - // If you have external dependencies installed from - // npm, you'll most likely need these plugins. In - // some cases you'll need additional configuration - - // consult the documentation for details: - // https://github.com/rollup/plugins/tree/master/packages/commonjs - resolve({ - preferBuiltins: false, - browser: true, - dedupe: ["svelte"], - }), - commonjs(), - typescript({ - sourceMap: !production, - inlineSources: !production, - }), - json(), - injectProcessEnv({ - DFX_NETWORK: process.env.DFX_NETWORK, - CANISTER_ID_ENCRYPTED_NOTES: process.env.CANISTER_ID_ENCRYPTED_NOTES, - }), - - // In dev mode, call `npm run start` once - // the bundle has been generated - !production && serve(exposeHost), - - // Watch the `public` directory and refresh the - // browser on changes when not in production - !production && livereload("public"), - - // If we're building for production (npm run build - // instead of npm run dev), minify - production && terser(), - ], - watch: { - clearScreen: false, - }, - onwarn: function (warning) { - if ( - [ - "CIRCULAR_DEPENDENCY", - "THIS_IS_UNDEFINED", - "EVAL", - "NAMESPACE_CONFLIC", - ].includes(warning.code) - ) { - return; - } - console.warn(warning.message); - }, - }; -}; diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/scripts/gen_bindings.sh b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/scripts/gen_bindings.sh deleted file mode 100755 index dbc07b9d7..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/scripts/gen_bindings.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -cd ../.. && dfx generate encrypted_notes || exit 1 - -rm -r frontend/src/declarations/encrypted_notes > /dev/null 2>&1 || true - -mkdir -p frontend/src/declarations/encrypted_notes -mv src/declarations/encrypted_notes frontend/src/declarations -rmdir -p src/declarations > /dev/null 2>&1 || true diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/actor.ts deleted file mode 100644 index 6c275f9e5..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/src/lib/actor.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { - Actor, - ActorConfig, - ActorSubclass, - HttpAgent, - HttpAgentOptions, -} from "@dfinity/agent"; -import { - idlFactory, - _SERVICE, -} from "../declarations/encrypted_notes/encrypted_notes.did.js"; - -export type BackendActor = ActorSubclass<_SERVICE>; - -export function createActor(options?: { - agentOptions?: HttpAgentOptions; - actorOptions?: ActorConfig; -}): BackendActor { - const hostOptions = { - host: - process.env.DFX_NETWORK === "ic" - ? `https://${process.env.CANISTER_ID_ENCRYPTED_NOTES}.ic0.app` - : "http://localhost:8000", - }; - if (!options) { - options = { - agentOptions: hostOptions, - }; - } else if (!options.agentOptions) { - options.agentOptions = hostOptions; - } else { - options.agentOptions.host = hostOptions.host; - } - - const agent = new HttpAgent({ ...options.agentOptions }); - // Fetch root key for certificate validation during development - if (process.env.NODE_ENV !== "production") { - console.log(`Dev environment - fetching root key...`); - - agent.fetchRootKey().catch((err) => { - console.warn( - "Unable to fetch root key. Check to ensure that your local replica is running" - ); - console.error(err); - }); - } - - // Creates an actor with using the candid interface and the HttpAgent - return Actor.createActor(idlFactory, { - agent, - canisterId: process.env.CANISTER_ID_ENCRYPTED_NOTES, - ...options?.actorOptions, - }); -} diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tailwind.config.js b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tailwind.config.js deleted file mode 100644 index e88d06d07..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tailwind.config.js +++ /dev/null @@ -1,10 +0,0 @@ -module.exports = { - content: [ - './public/index.html', - './src/**/*.svelte', - ], - theme: { - extend: {}, - }, - plugins: [require('daisyui'), require('@tailwindcss/line-clamp')], -}; diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tsconfig.json b/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tsconfig.json deleted file mode 100644 index 6bfdec1e8..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/frontend/tsconfig.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "extends": "@tsconfig/svelte/tsconfig.json", - "include": ["src/**/*"], - "exclude": ["node_modules/*", "__sapper__/*", "public/*", "src/declarations/**/*"] -} \ No newline at end of file diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/dfx.json b/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/dfx.json deleted file mode 100644 index 61d6507e6..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/motoko/dfx.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "canisters": { - "encrypted_notes": { - "main": "backend/main.mo", - "type": "motoko", - "args": "--enhanced-orthogonal-persistence", - "init_arg": "(\"test_key_1\")" - }, - "internet-identity": { - "candid": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity.did", - "type": "custom", - "specified_id": "rdmx6-jaaaa-aaaaa-aaadq-cai", - "remote": { - "id": { - "ic": "rdmx6-jaaaa-aaaaa-aaadq-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity_dev.wasm.gz" - }, - "www": { - "dependencies": ["encrypted_notes", "internet-identity"], - "build": [ - "cd frontend && npm i --include=dev && npm run build && cd - && rm -r public > /dev/null 2>&1; cp -r frontend/public ./" - ], - "frontend": { - "entrypoint": "public/index.html" - }, - "source": ["public/"], - "type": "assets", - "output_env_file": "frontend/.env" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - } - } \ No newline at end of file diff --git a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/dfx.json b/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/dfx.json deleted file mode 100644 index 7cfa041f3..000000000 --- a/rust/vetkeys/encrypted_notes_dapp_vetkd/rust/dfx.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "canisters": { - "encrypted_notes": { - "type": "rust", - "candid": "backend/src/encrypted_notes_rust.did", - "package": "encrypted_notes_backend", - "init_arg": "(\"test_key_1\")" - }, - "internet-identity": { - "candid": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity.did", - "type": "custom", - "specified_id": "rdmx6-jaaaa-aaaaa-aaadq-cai", - "remote": { - "id": { - "ic": "rdmx6-jaaaa-aaaaa-aaadq-cai" - } - }, - "wasm": "https://github.com/dfinity/internet-identity/releases/download/release-2026-03-16/internet_identity_dev.wasm.gz" - }, - "www": { - "dependencies": ["encrypted_notes", "internet-identity"], - "build": [ - "cd frontend && npm i --include=dev && npm run build && cd - && rm -r public > /dev/null 2>&1; cp -r frontend/public ./" - ], - "frontend": { - "entrypoint": "public/index.html" - }, - "source": ["public/"], - "type": "assets", - "output_env_file": "frontend/.env" - } - }, - "networks": { - "local": { - "bind": "127.0.0.1:8000", - "type": "ephemeral" - } - } -} From 4517514263001faabd8b4ff504eb55e82f71c352 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Sat, 30 May 2026 04:13:27 +0200 Subject: [PATCH 02/30] fix(ci): update encrypted_notes_app_vetkd to ic-cdk 0.20.1 and fix IC_ROOT_KEY - Remove rust-toolchain channel pin and profile - Bump ic-cdk to 0.20.1 and add ic-cdk-management-canister 0.1.1 - Update ic_cdk::management_canister imports to ic_cdk_management_canister - Fix ic_root_key -> IC_ROOT_KEY in actor.ts (CanisterEnv type uses uppercase) Co-Authored-By: Claude Sonnet 4.6 --- .../frontend/src/lib/actor.ts | 3 +-- .../frontend/src/store/auth.ts | 26 +++++++++---------- .../rust/backend/Cargo.toml | 3 ++- .../rust/backend/src/lib.rs | 6 ++--- .../rust/rust-toolchain.toml | 4 +-- 5 files changed, 19 insertions(+), 23 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts index 9e0e5bccd..fdd61fb41 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -6,7 +6,6 @@ export type BackendActor = ActorSubclass<_SERVICE>; const canisterEnv = safeGetCanisterEnv<{ "PUBLIC_CANISTER_ID:encrypted_notes": string; - ic_root_key?: string; }>(); export async function createActor(options?: { identity?: any }): Promise { @@ -14,7 +13,7 @@ export async function createActor(options?: { identity?: any }): Promise({ state: "initializing-auth" }); async function initAuth() { - const client = await AuthClient.create(); - if (await client.isAuthenticated()) { + const isLocal = + window.location.hostname === "localhost" || + window.location.hostname.endsWith(".localhost"); + const client = new AuthClient({ + identityProvider: isLocal ? "http://id.ai.localhost:8000/#authorize" : undefined, + }); + if (client.isAuthenticated()) { authenticate(client); } else { const actor = await createActor(); @@ -31,19 +36,12 @@ async function initAuth() { initAuth(); -export function login() { +export async function login() { const currentAuth = get(auth); if (currentAuth.state === "anonymous") { - currentAuth.client.login({ - maxTimeToLive: BigInt(1800) * BigInt(1_000_000_000), - identityProvider: - window.location.hostname === "localhost" || - window.location.hostname.endsWith(".localhost") - ? `http://id.ai.localhost:8000/#authorize` - : "https://identity.ic0.app/#authorize", - onSuccess: () => authenticate(currentAuth.client), - }); + await currentAuth.client.signIn(); + authenticate(currentAuth.client); } } @@ -51,7 +49,7 @@ export async function logout() { const currentAuth = get(auth); if (currentAuth.state === "initialized") { - await currentAuth.client.logout(); + await currentAuth.client.signOut(); const actor = await createActor(); auth.update(() => ({ state: "anonymous", @@ -66,7 +64,7 @@ export async function authenticate(client: AuthClient) { handleSessionTimeout(); try { - const actor = await createActor({ identity: client.getIdentity() }); + const actor = await createActor({ identity: await client.getIdentity() }); auth.update(() => ({ state: "initializing-crypto", diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml index 25355a7fa..48c9a0a7b 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/Cargo.toml @@ -9,7 +9,8 @@ crate-type = ["cdylib"] [dependencies] candid = "0.10" -ic-cdk = "0.18.3" +ic-cdk = "0.20.1" +ic-cdk-management-canister = "0.1.1" ic-stable-structures = "0.6.4" lazy_static = "1.4.0" serde_json = "1.0.108" diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs index 4ea54bd55..3bd0f2538 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/backend/src/lib.rs @@ -371,7 +371,7 @@ fn remove_user(note_id: NoteId, user: PrincipalName) { }); } -use ic_cdk::management_canister::{ +use ic_cdk_management_canister::{ VetKDCurve, VetKDDeriveKeyArgs, VetKDDeriveKeyResult, VetKDKeyId, VetKDPublicKeyArgs, VetKDPublicKeyResult, }; @@ -384,7 +384,7 @@ async fn symmetric_key_verification_key_for_note() -> String { key_id: key_id(), }; - let response: VetKDPublicKeyResult = ic_cdk::management_canister::vetkd_public_key(&request) + let response: VetKDPublicKeyResult = ic_cdk_management_canister::vetkd_public_key(&request) .await .expect("call to vetkd_public_key failed"); @@ -418,7 +418,7 @@ async fn encrypted_symmetric_key_for_note( } }); - let response: VetKDDeriveKeyResult = ic_cdk::management_canister::vetkd_derive_key(&request) + let response: VetKDDeriveKeyResult = ic_cdk_management_canister::vetkd_derive_key(&request) .await .expect("call to vetkd_derive_key failed"); diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml index 2a2058b04..6e5beca4f 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/rust-toolchain.toml @@ -1,4 +1,2 @@ [toolchain] -channel = "1.88.0" -targets = ["wasm32-unknown-unknown"] -profile = "default" \ No newline at end of file +targets = ["wasm32-unknown-unknown"] \ No newline at end of file From 3bf82f407770735eab4d1c1b7e742497fee8c4c0 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 10:29:46 +0200 Subject: [PATCH 03/30] fix(encrypted_notes): gen_bindings symlink, npm ci, motoko base dep - Use pwd -P in gen_bindings.sh to resolve paths correctly through symlinks - Change npm ci to npm install --include=dev (no package-lock.json committed) - Add base = "0.11.2" to motoko/mops.toml so mo:base imports resolve Co-Authored-By: Claude Sonnet 4.6 --- .../frontend/scripts/gen_bindings.sh | 17 +++++++++++++---- .../encrypted_notes_app_vetkd/motoko/icp.yaml | 2 +- .../encrypted_notes_app_vetkd/motoko/mops.toml | 3 +++ .../encrypted_notes_app_vetkd/rust/icp.yaml | 2 +- 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh index 521c6cece..41814be51 100755 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh @@ -1,9 +1,18 @@ #!/bin/bash +set -eu + +# Resolve the script's physical location so we work correctly even when the +# icp CLI has symlinked `frontend/` into a backend subdirectory for the build. +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)" +FRONTEND_DIR="$(dirname "$SCRIPT_DIR")" +EXAMPLE_ROOT="$(dirname "$FRONTEND_DIR")" # Bindings are always generated from the Rust backend since both backends # expose the same Candid interface. -cd ../.. -rm -rf frontend/src/declarations/encrypted_notes -mkdir -p frontend/src/declarations/encrypted_notes -npx @icp-sdk/bindgen --did-file rust/backend/src/encrypted_notes_rust.did --out-dir frontend/src/declarations/encrypted_notes --declarations-flat --force +rm -rf "$FRONTEND_DIR/src/declarations/encrypted_notes" +mkdir -p "$FRONTEND_DIR/src/declarations/encrypted_notes" +npx @icp-sdk/bindgen \ + --did-file "$EXAMPLE_ROOT/rust/backend/src/encrypted_notes_rust.did" \ + --out-dir "$FRONTEND_DIR/src/declarations/encrypted_notes" \ + --declarations-flat --force diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml index 3378b091a..59fa5f43e 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml @@ -14,7 +14,7 @@ canisters: configuration: dir: dist build: - - npm ci --prefix ../frontend + - npm install --include=dev --prefix ../frontend - npm run build --prefix ../frontend - rm -rf dist && mv ../frontend/dist dist diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml index b227db842..7f9ece274 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml @@ -1,2 +1,5 @@ [toolchain] moc = "1.5.1" + +[dependencies] +base = "0.11.2" diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml index 6d868c9f1..d36f90588 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml @@ -15,7 +15,7 @@ canisters: configuration: dir: dist build: - - npm ci --prefix ../frontend + - npm install --include=dev --prefix ../frontend - npm run build --prefix ../frontend - rm -rf dist && mv ../frontend/dist dist From 102339c3f22366eef2817b6e6d21bb29146686d5 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 10:51:40 +0200 Subject: [PATCH 04/30] fix(encrypted_notes): use --legacy-peer-deps for npm install svelte-router-spa@6.0.3 declares a peer dep on svelte@^3 but the frontend uses svelte 4. Adding --legacy-peer-deps allows npm to install without strict peer dep enforcement. Co-Authored-By: Claude Sonnet 4.6 --- rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml | 2 +- rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml index 59fa5f43e..bd9254a39 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml @@ -14,7 +14,7 @@ canisters: configuration: dir: dist build: - - npm install --include=dev --prefix ../frontend + - npm install --include=dev --legacy-peer-deps --prefix ../frontend - npm run build --prefix ../frontend - rm -rf dist && mv ../frontend/dist dist diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml index d36f90588..cf25e37b4 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml @@ -15,7 +15,7 @@ canisters: configuration: dir: dist build: - - npm install --include=dev --prefix ../frontend + - npm install --include=dev --legacy-peer-deps --prefix ../frontend - npm run build --prefix ../frontend - rm -rf dist && mv ../frontend/dist dist From 5b2930365ccb3e10f18eb928ef1b872085b160c0 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 11:08:17 +0200 Subject: [PATCH 05/30] fix(encrypted_notes): import from encrypted_notes_rust.did in actor.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bindgen generates the output file named after the input DID file. The Rust backend DID is encrypted_notes_rust.did, so the generated binding is encrypted_notes_rust.did.ts — not backend.did.ts. Co-Authored-By: Claude Sonnet 4.6 --- .../vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts index fdd61fb41..3d402a9a0 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -1,6 +1,6 @@ import { Actor, HttpAgent, type ActorSubclass } from "@icp-sdk/core/agent"; import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { idlFactory, type _SERVICE } from "../declarations/encrypted_notes/backend.did"; +import { idlFactory, type _SERVICE } from "../declarations/encrypted_notes/encrypted_notes_rust.did"; export type BackendActor = ActorSubclass<_SERVICE>; From 36a907a5e5e3d174031485276deb4b7c1db8eccf Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 11:51:31 +0200 Subject: [PATCH 06/30] chore(encrypted_notes): rename CI job keys to remove rust-vetkeys- prefix Co-Authored-By: Claude Sonnet 4.6 --- .../rust-vetkeys-encrypted-notes-app-vetkd-example.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml index 051d203d9..2c60e01ac 100644 --- a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml +++ b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml @@ -8,13 +8,13 @@ on: - rust/vetkeys/encrypted_notes_app_vetkd/** - .github/workflows/provision-darwin.sh - .github/workflows/provision-linux.sh - - .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml + - .github/workflows/encrypted-notes-app-vetkd-example.yml - .ic-commit concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - rust-vetkeys-encrypted-notes-app-vetkd-rust-darwin: + encrypted-notes-app-vetkd-rust-darwin: runs-on: macos-15 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -36,7 +36,7 @@ jobs: icp network start -d && icp deploy popd - rust-vetkeys-encrypted-notes-app-vetkd-motoko-darwin: + encrypted-notes-app-vetkd-motoko-darwin: runs-on: macos-15 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -58,7 +58,7 @@ jobs: icp network start -d && icp deploy popd - rust-vetkeys-encrypted-notes-app-vetkd-rust-linux: + encrypted-notes-app-vetkd-rust-linux: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -80,7 +80,7 @@ jobs: icp network start -d && icp deploy popd - rust-vetkeys-encrypted-notes-app-vetkd-motoko-linux: + encrypted-notes-app-vetkd-motoko-linux: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 From 067ffb6ec62b128e4239d8d1c03159f2cb39e8e1 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 11:56:41 +0200 Subject: [PATCH 07/30] chore(encrypted_notes): rename CI jobs to language-first format Co-Authored-By: Claude Sonnet 4.6 --- .../rust-vetkeys-encrypted-notes-app-vetkd-example.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml index 2c60e01ac..9ea7c2655 100644 --- a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml +++ b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml @@ -8,13 +8,13 @@ on: - rust/vetkeys/encrypted_notes_app_vetkd/** - .github/workflows/provision-darwin.sh - .github/workflows/provision-linux.sh - - .github/workflows/encrypted-notes-app-vetkd-example.yml + - .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml - .ic-commit concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - encrypted-notes-app-vetkd-rust-darwin: + rust-encrypted-notes-app-vetkd-darwin: runs-on: macos-15 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -36,7 +36,7 @@ jobs: icp network start -d && icp deploy popd - encrypted-notes-app-vetkd-motoko-darwin: + motoko-encrypted-notes-app-vetkd-darwin: runs-on: macos-15 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -58,7 +58,7 @@ jobs: icp network start -d && icp deploy popd - encrypted-notes-app-vetkd-rust-linux: + rust-encrypted-notes-app-vetkd-linux: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 @@ -80,7 +80,7 @@ jobs: icp network start -d && icp deploy popd - encrypted-notes-app-vetkd-motoko-linux: + motoko-encrypted-notes-app-vetkd-linux: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 From ec295acffc0062ce2358b0ad0823f1995a0811dc Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 13:57:18 +0200 Subject: [PATCH 08/30] fix: use key_1 (not test_key_1), npx --yes, console.error in basic_ibe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change test_key_1 → key_1 in all icp.yaml: icp-cli 0.2.7 only provisions vetkd:Bls12_381_G2:key_1 (TestThresholdKeys subnet with test_key_1 is not yet supported by icp-cli). The vetkeys library itself uses key_1 in its own icp.yaml files. - Add --yes flag to npx @icp-sdk/bindgen to suppress the install confirmation prompt during local builds - basic_ibe: wrap event listeners in try-catch so errors are caught and shown (not silent unhandled rejections); add console.error() alongside alert() so errors are visible and copyable in DevTools Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh | 2 +- rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml | 2 +- rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh index 41814be51..895b957ab 100755 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/scripts/gen_bindings.sh @@ -12,7 +12,7 @@ EXAMPLE_ROOT="$(dirname "$FRONTEND_DIR")" rm -rf "$FRONTEND_DIR/src/declarations/encrypted_notes" mkdir -p "$FRONTEND_DIR/src/declarations/encrypted_notes" -npx @icp-sdk/bindgen \ +npx --yes @icp-sdk/bindgen \ --did-file "$EXAMPLE_ROOT/rust/backend/src/encrypted_notes_rust.did" \ --out-dir "$FRONTEND_DIR/src/declarations/encrypted_notes" \ --declarations-flat --force diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml index bd9254a39..eb50c22da 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml @@ -6,7 +6,7 @@ canisters: main: backend/main.mo init_args: type: text - value: "(\"test_key_1\")" + value: "(\"key_1\")" - name: www recipe: diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml index cf25e37b4..9af37d235 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml @@ -7,7 +7,7 @@ canisters: candid: backend/src/encrypted_notes_rust.did init_args: type: text - value: "(\"test_key_1\")" + value: "(\"key_1\")" - name: www recipe: From 61471bdb59e7b099ebb4be0594ab1be2cce40417 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 15:40:31 +0200 Subject: [PATCH 09/30] refactor: use generated createActor and Backend type from declarations Replace manual Actor.createActor(idlFactory, ...) with the generated createActor(canisterId, { agent }) function and typed Backend wrapper class from the bindgen-generated declarations. Removes direct imports of idlFactory, Actor, and ActorSubclass. Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/frontend/src/lib/actor.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts index 3d402a9a0..ccecf3daf 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -1,8 +1,8 @@ -import { Actor, HttpAgent, type ActorSubclass } from "@icp-sdk/core/agent"; +import { HttpAgent } from "@icp-sdk/core/agent"; import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { idlFactory, type _SERVICE } from "../declarations/encrypted_notes/encrypted_notes_rust.did"; +import { createActor as createEncryptedNotesActor, type Backend } from "../declarations/encrypted_notes/encrypted_notes_rust.did"; -export type BackendActor = ActorSubclass<_SERVICE>; +export type BackendActor = Backend; const canisterEnv = safeGetCanisterEnv<{ "PUBLIC_CANISTER_ID:encrypted_notes": string; @@ -15,5 +15,5 @@ export async function createActor(options?: { identity?: any }): Promise Date: Mon, 1 Jun 2026 16:42:08 +0200 Subject: [PATCH 10/30] fix(encrypted_notes): import createActor from wrapper, not IDL file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit encrypted_notes_rust.did.ts is the IDL-only file (no createActor). The TypeScript wrapper with createActor and Backend is at encrypted_notes_rust.ts — imported without the .did suffix. Co-Authored-By: Claude Sonnet 4.6 --- .../vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts index ccecf3daf..cd9f89e1a 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -1,6 +1,6 @@ import { HttpAgent } from "@icp-sdk/core/agent"; import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"; -import { createActor as createEncryptedNotesActor, type Backend } from "../declarations/encrypted_notes/encrypted_notes_rust.did"; +import { createActor as createEncryptedNotesActor, type Backend } from "../declarations/encrypted_notes/encrypted_notes_rust"; export type BackendActor = Backend; From f0a721cb24dbf03b5650238dfae8ca1cfe4649d5 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Mon, 1 Jun 2026 17:46:05 +0200 Subject: [PATCH 11/30] docs: add folder structure, fix dev command and cd instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Folder Structure section explaining rust/, motoko/, frontend/ layout and why backends are in subdirectories (shared frontend via symlinks) - Replace vague "from the X folder" wording with explicit `cd X` commands - Fix `npm run dev` → `npm run dev:rust` / `npm run dev:motoko` (bare `dev` script intentionally errors with a "specify a backend" message) - Add `cd frontend` before all dev commands (package.json is in frontend/, not at the example root) Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/README.md | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/README.md b/rust/vetkeys/encrypted_notes_app_vetkd/README.md index ab4cd89ef..948605b4a 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/README.md +++ b/rust/vetkeys/encrypted_notes_app_vetkd/README.md @@ -18,24 +18,39 @@ The vetKey used to encrypt and decrypt a note is note-ID-specific (and not, for - [x] Install the [ICP CLI](https://cli.internetcomputer.org). - [x] Install [npm](https://www.npmjs.com/package/npm). +## Folder Structure + +This example provides both a **Rust** and a **Motoko** backend, sharing a common `frontend/`: + +``` +encrypted_notes_app_vetkd/ +├── frontend/ ← shared frontend (symlinked into rust/ and motoko/) +├── motoko/ ← Motoko backend + icp.yaml +└── rust/ ← Rust backend + icp.yaml +``` + ## Deploy the Canisters Locally -To deploy with the Motoko backend, run from the `motoko` folder: +Deploy with the **Motoko** backend: ```bash +cd motoko icp network start -d && icp deploy ``` -To deploy with the Rust backend instead, run the same command from the `rust` folder. +Or deploy with the **Rust** backend: +```bash +cd rust +icp network start -d && icp deploy +``` ## Running the Frontend in Development Mode After deploying, run from the `frontend` folder: ```bash -# For Motoko backend: -npm run dev:motoko - -# For Rust backend: -npm run dev:rust +cd frontend +npm run dev:motoko # if you deployed the Motoko backend +# or +npm run dev:rust # if you deployed the Rust backend ``` ## Example Components From 288f1725ffd3b716fbd208bb5328eadf91f464db Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 2 Jun 2026 14:05:01 +0200 Subject: [PATCH 12/30] fix: simplify rootKey; use /authorize (not /#authorize) and https://id.ai - rootKey: pass rootKey: canisterEnv?.IC_ROOT_KEY directly; undefined is fine when on mainnet (HttpAgent ignores it) - identityProvider local: use /authorize path instead of /#authorize hash - identityProvider production: use https://id.ai instead of undefined Co-Authored-By: Claude Sonnet 4.6 --- .../vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts | 2 +- .../encrypted_notes_app_vetkd/frontend/src/store/auth.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts index cd9f89e1a..b29294c5c 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/lib/actor.ts @@ -13,7 +13,7 @@ export async function createActor(options?: { identity?: any }): Promise Date: Tue, 2 Jun 2026 14:06:22 +0200 Subject: [PATCH 13/30] fix: use https://id.ai/authorize for production identityProvider Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/frontend/src/store/auth.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts index 2ae43f37a..ca1af79dd 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts @@ -20,7 +20,7 @@ async function initAuth() { window.location.hostname === "localhost" || window.location.hostname.endsWith(".localhost"); const client = new AuthClient({ - identityProvider: isLocal ? "http://id.ai.localhost:8000/authorize" : "https://id.ai", + identityProvider: isLocal ? "http://id.ai.localhost:8000/authorize" : "https://id.ai/authorize", }); if (client.isAuthenticated()) { authenticate(client); From 82dcf95d0c71f1dcce1e0633d1486fb3c58436ff Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 2 Jun 2026 16:16:33 +0200 Subject: [PATCH 14/30] chore: migrate Motoko backend from base to core 2.5.0 Co-Authored-By: Claude Sonnet 4.6 --- .../motoko/backend/main.mo | 175 +++++++++--------- .../motoko/mops.toml | 4 +- 2 files changed, 85 insertions(+), 94 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo index 52b99f6e2..b6f5f9c42 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo @@ -1,17 +1,16 @@ -import Map "mo:base/HashMap"; -import Text "mo:base/Text"; -import Array "mo:base/Array"; -import Buffer "mo:base/Buffer"; -import List "mo:base/List"; -import Iter "mo:base/Iter"; -import Nat "mo:base/Nat"; -import Nat8 "mo:base/Nat8"; -import Bool "mo:base/Bool"; -import Principal "mo:base/Principal"; -import Option "mo:base/Option"; -import Debug "mo:base/Debug"; -import Blob "mo:base/Blob"; -import Hash "mo:base/Hash"; +import Map "mo:core/Map"; +import Text "mo:core/Text"; +import Array "mo:core/Array"; +import List "mo:core/List"; +import PureList "mo:core/pure/List"; +import Iter "mo:core/Iter"; +import Nat "mo:core/Nat"; +import Nat8 "mo:core/Nat8"; +import Bool "mo:core/Bool"; +import Principal "mo:core/Principal"; +import Option "mo:core/Option"; +import Debug "mo:core/Debug"; +import Blob "mo:core/Blob"; import Hex "./utils/Hex"; // Declare a shared actor class @@ -61,11 +60,11 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { private var nextNoteId : Nat = 1; // Store notes by their ID, so that note-specific encryption keys can be derived. - private transient var notesById = Map.HashMap(0, Nat.equal, Hash.hash); + private transient var notesById = Map.empty(); // Store which note IDs are owned by a particular principal - private transient var noteIdsByOwner = Map.HashMap>(0, Text.equal, Text.hash); + private transient var noteIdsByOwner = Map.empty>(); // Store which notes are shared with a particular principal. Does not include the owner, as this is tracked by `noteIdsByOwner`. - private transient var noteIdsByUser = Map.HashMap>(0, Text.equal, Text.hash); + private transient var noteIdsByUser = Map.empty>(); // While accessing _heap_ data is more efficient, we use the following _stable memory_ // as a buffer to preserve data across canister upgrades. @@ -73,8 +72,8 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { // https://internetcomputer.org/docs/current/developer-docs/production/resource-limits. // See also: [preupgrade], [postupgrade] private var stable_notesById : [(NoteId, EncryptedNote)] = []; - private var stable_noteIdsByOwner : [(PrincipalName, List.List)] = []; - private var stable_noteIdsByUser : [(PrincipalName, List.List)] = []; + private var stable_noteIdsByOwner : [(PrincipalName, PureList.List)] = []; + private var stable_noteIdsByUser : [(PrincipalName, PureList.List)] = []; // Utility function that helps writing assertion-driven code more concisely. private func expect(opt : ?T, violation_msg : Text) : T { @@ -120,18 +119,18 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { users = []; }; - switch (noteIdsByOwner.get(owner)) { + switch (Map.get(noteIdsByOwner, Text.compare, owner)) { case (?owner_nids) { - assert List.size(owner_nids) < MAX_NOTES_PER_USER; - noteIdsByOwner.put(owner, List.push(newNote.id, owner_nids)); + assert PureList.size(owner_nids) < MAX_NOTES_PER_USER; + ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.push(newNote.id, owner_nids)); }; case null { - assert noteIdsByOwner.size() < MAX_USERS; - noteIdsByOwner.put(owner, List.make(newNote.id)); + assert Map.size(noteIdsByOwner) < MAX_USERS; + ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.make(newNote.id)); }; }; - notesById.put(newNote.id, newNote); + ignore Map.insert(notesById, Nat.compare, newNote.id, newNote); nextNoteId += 1; newNote.id; }; @@ -156,23 +155,23 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { assert not Principal.isAnonymous(caller); let user = Principal.toText(caller); - let owned_notes = List.map( - Option.get(noteIdsByOwner.get(user), List.nil()), + let owned_notes = PureList.map( + Option.get(Map.get(noteIdsByOwner, Text.compare, user), PureList.nil()), func(nid : NoteId) : EncryptedNote { - expect(notesById.get(nid), "missing note with ID " # Nat.toText(nid)); + expect(Map.get(notesById, Nat.compare, nid), "missing note with ID " # Nat.toText(nid)); }, ); - let shared_notes = List.map( - Option.get(noteIdsByUser.get(user), List.nil()), + let shared_notes = PureList.map( + Option.get(Map.get(noteIdsByUser, Text.compare, user), PureList.nil()), func(nid : NoteId) : EncryptedNote { - expect(notesById.get(nid), "missing note with ID " # Nat.toText(nid)); + expect(Map.get(notesById, Nat.compare, nid), "missing note with ID " # Nat.toText(nid)); }, ); - let buf = Buffer.Buffer(List.size(owned_notes) + List.size(shared_notes)); - buf.append(Buffer.fromArray(List.toArray(owned_notes))); - buf.append(Buffer.fromArray(List.toArray(shared_notes))); - Buffer.toArray(buf); + let buf = List.empty(); + List.append(buf, List.fromArray(PureList.toArray(owned_notes))); + List.append(buf, List.fromArray(PureList.toArray(shared_notes))); + List.toArray(buf); }; // Replaces the encrypted text of note with ID [id] with [encrypted_text]. @@ -187,12 +186,12 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func update_note(id : NoteId, encrypted_text : Text) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note_to_update) = notesById.get(id) else Debug.trap("note with id " # Nat.toText(id) # "not found"); + let (?note_to_update) = Map.get(notesById, Nat.compare, id) else Debug.trap("note with id " # Nat.toText(id) # "not found"); if (not is_authorized(caller_text, note_to_update)) { Debug.trap("unauthorized"); }; assert note_to_update.encrypted_text.size() <= MAX_NOTE_CHARS; - notesById.put(id, { note_to_update with encrypted_text }); + ignore Map.insert(notesById, Nat.compare, id, { note_to_update with encrypted_text }); }; // Shares the note with ID [note_id] with the [user]. @@ -207,25 +206,25 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func add_user(note_id : NoteId, user : PrincipalName) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note) = notesById.get(note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); if (caller_text != note.owner) { Debug.trap("unauthorized"); }; assert note.users.size() < MAX_SHARES_PER_NOTE; if (not Option.isSome(Array.find(note.users, func(u : PrincipalName) : Bool { u == user }))) { - let users_buf = Buffer.fromArray(note.users); - users_buf.add(user); - let updated_note = { note with users = Buffer.toArray(users_buf) }; - notesById.put(note_id, updated_note); + let users_buf = List.fromArray(note.users); + List.add(users_buf, user); + let updated_note = { note with users = List.toArray(users_buf) }; + ignore Map.insert(notesById, Nat.compare, note_id, updated_note); }; - switch (noteIdsByUser.get(user)) { + switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { - if (not List.some(user_nids, func(nid : NoteId) : Bool { nid == note_id })) { - noteIdsByUser.put(user, List.push(note_id, user_nids)); + if (not PureList.some(user_nids, func(nid : NoteId) : Bool { nid == note_id })) { + ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.push(note_id, user_nids)); }; }; case null { - noteIdsByUser.put(user, List.make(note_id)); + ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.make(note_id)); }; }; }; @@ -242,22 +241,20 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func remove_user(note_id : NoteId, user : PrincipalName) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note) = notesById.get(note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); if (caller_text != note.owner) { Debug.trap("unauthorized"); }; - let users_buf = Buffer.fromArray(note.users); - users_buf.filterEntries(func(i : Nat, u : PrincipalName) : Bool { u != user }); - let updated_note = { note with users = Buffer.toArray(users_buf) }; - notesById.put(note_id, updated_note); + let updated_note = { note with users = Array.filter(note.users, func(u : PrincipalName) : Bool { u != user }) }; + ignore Map.insert(notesById, Nat.compare, note_id, updated_note); - switch (noteIdsByUser.get(user)) { + switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { - let updated_nids = List.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not List.isNil(updated_nids)) { - noteIdsByUser.put(user, updated_nids); + let updated_nids = PureList.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); + if (not PureList.isNil(updated_nids)) { + ignore Map.insert(noteIdsByUser, Text.compare, user, updated_nids); } else { - let _ = noteIdsByUser.remove(user); + ignore Map.remove(noteIdsByUser, Text.compare, user); }; }; case null {}; @@ -275,36 +272,36 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func delete_note(note_id : NoteId) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note_to_delete) = notesById.get(note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note_to_delete) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); let owner = note_to_delete.owner; if (owner != caller_text) { Debug.trap("unauthorized"); }; - switch (noteIdsByOwner.get(owner)) { + switch (Map.get(noteIdsByOwner, Text.compare, owner)) { case (?owner_nids) { - let updated_nids = List.filter(owner_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not List.isNil(updated_nids)) { - noteIdsByOwner.put(owner, updated_nids); + let updated_nids = PureList.filter(owner_nids, func(nid : NoteId) : Bool { nid != note_id }); + if (not PureList.isNil(updated_nids)) { + ignore Map.insert(noteIdsByOwner, Text.compare, owner, updated_nids); } else { - let _ = noteIdsByOwner.remove(owner); + ignore Map.remove(noteIdsByOwner, Text.compare, owner); }; }; case null {}; }; - for (user in note_to_delete.users.vals()) { - switch (noteIdsByUser.get(user)) { + for (user in note_to_delete.users.values()) { + switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { - let updated_nids = List.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not List.isNil(updated_nids)) { - noteIdsByUser.put(user, updated_nids); + let updated_nids = PureList.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); + if (not PureList.isNil(updated_nids)) { + ignore Map.insert(noteIdsByUser, Text.compare, user, updated_nids); } else { - let _ = noteIdsByUser.remove(user); + ignore Map.remove(noteIdsByUser, Text.compare, user); }; }; case null {}; }; }; - let _ = notesById.remove(note_id); + ignore Map.remove(notesById, Nat.compare, note_id); }; // Only the vetKD methods in the IC management canister are required here. @@ -335,15 +332,15 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func encrypted_symmetric_key_for_note(note_id : NoteId, transport_public_key : Blob) : async Text { let caller_text = Principal.toText(caller); - let (?note) = notesById.get(note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); if (not is_authorized(caller_text, note)) { Debug.trap("unauthorized"); }; - let buf = Buffer.Buffer(32); - buf.append(Buffer.fromArray(natToBigEndianByteArray(16, note_id))); // fixed-size encoding - buf.append(Buffer.fromArray(Blob.toArray(Text.encodeUtf8(note.owner)))); - let input = Blob.fromArray(Buffer.toArray(buf)); // prefix-free + let buf = List.empty(); + List.append(buf, List.fromArray(natToBigEndianByteArray(16, note_id))); // fixed-size encoding + List.append(buf, List.fromArray(Blob.toArray(Text.encodeUtf8(note.owner)))); + let input = Blob.fromArray(List.toArray(buf)); // prefix-free let { encrypted_key } = await (with cycles = 26_153_846_153) management_canister.vetkd_derive_key({ input; @@ -370,9 +367,9 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { // The work required before a canister upgrade begins. system func preupgrade() { Debug.print("Starting pre-upgrade hook..."); - stable_notesById := Iter.toArray(notesById.entries()); - stable_noteIdsByOwner := Iter.toArray(noteIdsByOwner.entries()); - stable_noteIdsByUser := Iter.toArray(noteIdsByUser.entries()); + stable_notesById := Iter.toArray(Map.entries(notesById)); + stable_noteIdsByOwner := Iter.toArray(Map.entries(noteIdsByOwner)); + stable_noteIdsByUser := Iter.toArray(Map.entries(noteIdsByUser)); Debug.print("pre-upgrade finished."); }; @@ -381,27 +378,21 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { system func postupgrade() { Debug.print("Starting post-upgrade hook..."); - notesById := Map.fromIter( - stable_notesById.vals(), - stable_notesById.size(), - Nat.equal, - Hash.hash, + notesById := Map.fromIter( + stable_notesById.values(), + Nat.compare, ); stable_notesById := []; - noteIdsByOwner := Map.fromIter>( - stable_noteIdsByOwner.vals(), - stable_noteIdsByOwner.size(), - Text.equal, - Text.hash, + noteIdsByOwner := Map.fromIter( + stable_noteIdsByOwner.values(), + Text.compare, ); stable_noteIdsByOwner := []; - noteIdsByUser := Map.fromIter>( - stable_noteIdsByUser.vals(), - stable_noteIdsByUser.size(), - Text.equal, - Text.hash, + noteIdsByUser := Map.fromIter( + stable_noteIdsByUser.values(), + Text.compare, ); stable_noteIdsByUser := []; diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml index 7f9ece274..1172ef015 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/mops.toml @@ -1,5 +1,5 @@ [toolchain] -moc = "1.5.1" +moc = "1.9.0" [dependencies] -base = "0.11.2" +core = "2.5.0" From 6b9099c691f0f44db42eed48aad495011f6e688c Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 2 Jun 2026 16:57:54 +0200 Subject: [PATCH 15/30] =?UTF-8?q?fix:=20correct=20base=E2=86=92core=20API?= =?UTF-8?q?=20issues=20in=20Motoko=20backends?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit password_manager_with_metadata: pure/Map has no Make factory — pass compareMetadataKeys directly to each operation; fix .vals() → .values() encrypted_notes Hex.mo: migrate mo:base imports to mo:core; fix Array.init(n,0)→Array.repeat(0,n), Array.freeze→Array.fromVarArray, Iter.range(0,15)→Iter.range(0,16) (core range is exclusive) Co-Authored-By: Claude Sonnet 4.6 --- .../motoko/backend/utils/Hex.mo | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo index 310b72d6d..413cc18ba 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo @@ -5,13 +5,13 @@ * License : Apache 2.0> */ -import Array "mo:base/Array"; -import Iter "mo:base/Iter"; -import Option "mo:base/Option"; -import Nat8 "mo:base/Nat8"; -import Char "mo:base/Char"; -import Result "mo:base/Result"; -import Text "mo:base/Text"; +import Array "mo:core/Array"; +import Iter "mo:core/Iter"; +import Option "mo:core/Option"; +import Nat8 "mo:core/Nat8"; +import Char "mo:core/Char"; +import Result "mo:core/Result"; +import Text "mo:core/Text"; import Prim "mo:⛔"; module { @@ -75,7 +75,7 @@ module { }; var i = 0; let n = upper.size() / 2 + upper.size() % 2; - let array = Array.init(n, 0); + let array = Array.repeat(0, n); while (i != n) { switch (parse()) { case (#ok w8) { @@ -87,14 +87,14 @@ module { }; }; }; - #ok (Array.freeze(array)); + #ok (Array.fromVarArray(array)); }; /** * Decode an unsigned 4-bit integer in hexadecimal format. */ private func decodeW4(char : Char) : Result { - for (i in Iter.range(0, 15)) { + for (i in Iter.range(0, 16)) { if (symbols[i] == char) { return #ok (Nat8.fromNat(i)); }; From c84599ccf933152c454eb95faa2c70101a76a5f6 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Tue, 2 Jun 2026 17:14:16 +0200 Subject: [PATCH 16/30] fix: correct pure/Map and VarArray API usage in Motoko backends MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit password_manager_with_metadata: - Debug.trap → Runtime.trap (trap moved to Runtime module) - OrderedMap.put → OrderedMap.add (add returns Map directly, no tuple) - OrderedMap.delete → OrderedMap.remove (remove returns Map directly) encrypted_notes Hex.mo: - Array.repeat returns immutable [T]; use VarArray.repeat for [var T] - Iter.range moved to Nat.range in core Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo index 413cc18ba..b96543458 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/utils/Hex.mo @@ -7,6 +7,8 @@ import Array "mo:core/Array"; import Iter "mo:core/Iter"; +import Nat "mo:core/Nat"; +import VarArray "mo:core/VarArray"; import Option "mo:core/Option"; import Nat8 "mo:core/Nat8"; import Char "mo:core/Char"; @@ -75,7 +77,7 @@ module { }; var i = 0; let n = upper.size() / 2 + upper.size() % 2; - let array = Array.repeat(0, n); + let array = VarArray.repeat(0, n); while (i != n) { switch (parse()) { case (#ok w8) { @@ -94,7 +96,7 @@ module { * Decode an unsigned 4-bit integer in hexadecimal format. */ private func decodeW4(char : Char) : Result { - for (i in Iter.range(0, 16)) { + for (i in Nat.range(0, 16)) { if (symbols[i] == char) { return #ok (Nat8.fromNat(i)); }; From 7d72998e9fd2a6ffec8f55db27830418db92fe4b Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 11:02:58 +0200 Subject: [PATCH 17/30] fix(encrypted_notes): correct pure/List and Runtime API usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Debug.trap → Runtime.trap (trap moved to Runtime module) - PureList.push(item, list) → PureList.pushFront(list, item) (renamed + reversed args) - PureList.make(item) → PureList.singleton(item) - PureList.nil() → PureList.empty() - PureList.some(list, f) → PureList.any(list, f) - PureList.isNil(list) → PureList.isEmpty(list) Co-Authored-By: Claude Sonnet 4.6 --- .../motoko/backend/main.mo | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo index b6f5f9c42..2e520b2bf 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo @@ -10,6 +10,7 @@ import Bool "mo:core/Bool"; import Principal "mo:core/Principal"; import Option "mo:core/Option"; import Debug "mo:core/Debug"; +import Runtime "mo:core/Runtime"; import Blob "mo:core/Blob"; import Hex "./utils/Hex"; @@ -79,7 +80,7 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { private func expect(opt : ?T, violation_msg : Text) : T { switch (opt) { case (null) { - Debug.trap(violation_msg); + Runtime.trap(violation_msg); }; case (?x) { x; @@ -122,11 +123,11 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { switch (Map.get(noteIdsByOwner, Text.compare, owner)) { case (?owner_nids) { assert PureList.size(owner_nids) < MAX_NOTES_PER_USER; - ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.push(newNote.id, owner_nids)); + ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.pushFront(owner_nids, newNote.id)); }; case null { assert Map.size(noteIdsByOwner) < MAX_USERS; - ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.make(newNote.id)); + ignore Map.insert(noteIdsByOwner, Text.compare, owner, PureList.singleton(newNote.id)); }; }; @@ -156,13 +157,13 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { let user = Principal.toText(caller); let owned_notes = PureList.map( - Option.get(Map.get(noteIdsByOwner, Text.compare, user), PureList.nil()), + Option.get(Map.get(noteIdsByOwner, Text.compare, user), PureList.empty()), func(nid : NoteId) : EncryptedNote { expect(Map.get(notesById, Nat.compare, nid), "missing note with ID " # Nat.toText(nid)); }, ); let shared_notes = PureList.map( - Option.get(Map.get(noteIdsByUser, Text.compare, user), PureList.nil()), + Option.get(Map.get(noteIdsByUser, Text.compare, user), PureList.empty()), func(nid : NoteId) : EncryptedNote { expect(Map.get(notesById, Nat.compare, nid), "missing note with ID " # Nat.toText(nid)); }, @@ -186,9 +187,9 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func update_note(id : NoteId, encrypted_text : Text) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note_to_update) = Map.get(notesById, Nat.compare, id) else Debug.trap("note with id " # Nat.toText(id) # "not found"); + let (?note_to_update) = Map.get(notesById, Nat.compare, id) else Runtime.trap("note with id " # Nat.toText(id) # "not found"); if (not is_authorized(caller_text, note_to_update)) { - Debug.trap("unauthorized"); + Runtime.trap("unauthorized"); }; assert note_to_update.encrypted_text.size() <= MAX_NOTE_CHARS; ignore Map.insert(notesById, Nat.compare, id, { note_to_update with encrypted_text }); @@ -206,9 +207,9 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func add_user(note_id : NoteId, user : PrincipalName) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Runtime.trap("note with id " # Nat.toText(note_id) # "not found"); if (caller_text != note.owner) { - Debug.trap("unauthorized"); + Runtime.trap("unauthorized"); }; assert note.users.size() < MAX_SHARES_PER_NOTE; if (not Option.isSome(Array.find(note.users, func(u : PrincipalName) : Bool { u == user }))) { @@ -219,12 +220,12 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { }; switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { - if (not PureList.some(user_nids, func(nid : NoteId) : Bool { nid == note_id })) { - ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.push(note_id, user_nids)); + if (not PureList.any(user_nids, func(nid : NoteId) : Bool { nid == note_id })) { + ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.pushFront(user_nids, note_id)); }; }; case null { - ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.make(note_id)); + ignore Map.insert(noteIdsByUser, Text.compare, user, PureList.singleton(note_id)); }; }; }; @@ -241,9 +242,9 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func remove_user(note_id : NoteId, user : PrincipalName) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Runtime.trap("note with id " # Nat.toText(note_id) # "not found"); if (caller_text != note.owner) { - Debug.trap("unauthorized"); + Runtime.trap("unauthorized"); }; let updated_note = { note with users = Array.filter(note.users, func(u : PrincipalName) : Bool { u != user }) }; ignore Map.insert(notesById, Nat.compare, note_id, updated_note); @@ -251,7 +252,7 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { let updated_nids = PureList.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not PureList.isNil(updated_nids)) { + if (not PureList.isEmpty(updated_nids)) { ignore Map.insert(noteIdsByUser, Text.compare, user, updated_nids); } else { ignore Map.remove(noteIdsByUser, Text.compare, user); @@ -272,15 +273,15 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func delete_note(note_id : NoteId) : async () { assert not Principal.isAnonymous(caller); let caller_text = Principal.toText(caller); - let (?note_to_delete) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note_to_delete) = Map.get(notesById, Nat.compare, note_id) else Runtime.trap("note with id " # Nat.toText(note_id) # "not found"); let owner = note_to_delete.owner; if (owner != caller_text) { - Debug.trap("unauthorized"); + Runtime.trap("unauthorized"); }; switch (Map.get(noteIdsByOwner, Text.compare, owner)) { case (?owner_nids) { let updated_nids = PureList.filter(owner_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not PureList.isNil(updated_nids)) { + if (not PureList.isEmpty(updated_nids)) { ignore Map.insert(noteIdsByOwner, Text.compare, owner, updated_nids); } else { ignore Map.remove(noteIdsByOwner, Text.compare, owner); @@ -292,7 +293,7 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { switch (Map.get(noteIdsByUser, Text.compare, user)) { case (?user_nids) { let updated_nids = PureList.filter(user_nids, func(nid : NoteId) : Bool { nid != note_id }); - if (not PureList.isNil(updated_nids)) { + if (not PureList.isEmpty(updated_nids)) { ignore Map.insert(noteIdsByUser, Text.compare, user, updated_nids); } else { ignore Map.remove(noteIdsByUser, Text.compare, user); @@ -332,9 +333,9 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { public shared ({ caller }) func encrypted_symmetric_key_for_note(note_id : NoteId, transport_public_key : Blob) : async Text { let caller_text = Principal.toText(caller); - let (?note) = Map.get(notesById, Nat.compare, note_id) else Debug.trap("note with id " # Nat.toText(note_id) # "not found"); + let (?note) = Map.get(notesById, Nat.compare, note_id) else Runtime.trap("note with id " # Nat.toText(note_id) # "not found"); if (not is_authorized(caller_text, note)) { - Debug.trap("unauthorized"); + Runtime.trap("unauthorized"); }; let buf = List.empty(); From 24edb68b4af6d1147dc1f2cc0e2107d4a50514f5 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 11:11:44 +0200 Subject: [PATCH 18/30] fix(encrypted_notes): add explicit type params to List.fromArray calls Type inference cannot determine T when the source is a PureList.toArray or Array result without context. Annotate all List.fromArray calls. Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/motoko/backend/main.mo | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo index 2e520b2bf..01abfd5c8 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/backend/main.mo @@ -170,8 +170,8 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { ); let buf = List.empty(); - List.append(buf, List.fromArray(PureList.toArray(owned_notes))); - List.append(buf, List.fromArray(PureList.toArray(shared_notes))); + List.append(buf, List.fromArray(PureList.toArray(owned_notes))); + List.append(buf, List.fromArray(PureList.toArray(shared_notes))); List.toArray(buf); }; @@ -339,8 +339,8 @@ shared ({ caller = initializer }) persistent actor class (keyName: Text) { }; let buf = List.empty(); - List.append(buf, List.fromArray(natToBigEndianByteArray(16, note_id))); // fixed-size encoding - List.append(buf, List.fromArray(Blob.toArray(Text.encodeUtf8(note.owner)))); + List.append(buf, List.fromArray(natToBigEndianByteArray(16, note_id))); // fixed-size encoding + List.append(buf, List.fromArray(Blob.toArray(Text.encodeUtf8(note.owner)))); let input = Blob.fromArray(List.toArray(buf)); // prefix-free let { encrypted_key } = await (with cycles = 26_153_846_153) management_canister.vetkd_derive_key({ From 1d47100ab274712639769306faa868b0cd148d69 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 11:21:25 +0200 Subject: [PATCH 19/30] fix: switch back to test_key_1 (supported again in latest icp-cli-network-launcher) Co-Authored-By: Claude Sonnet 4.6 --- rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml | 2 +- rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml index eb50c22da..bd9254a39 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/motoko/icp.yaml @@ -6,7 +6,7 @@ canisters: main: backend/main.mo init_args: type: text - value: "(\"key_1\")" + value: "(\"test_key_1\")" - name: www recipe: diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml index 9af37d235..cf25e37b4 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml +++ b/rust/vetkeys/encrypted_notes_app_vetkd/rust/icp.yaml @@ -7,7 +7,7 @@ canisters: candid: backend/src/encrypted_notes_rust.did init_args: type: text - value: "(\"key_1\")" + value: "(\"test_key_1\")" - name: www recipe: From 0e701772812e0fe56c1a2f21be288969673fd113 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 11:44:26 +0200 Subject: [PATCH 20/30] ci: switch to icp-dev-env container images, Ubuntu only Replace provision scripts + macOS + Linux dual-platform setup with single Ubuntu job using icp-dev-env Docker images: - ghcr.io/dfinity/icp-dev-env-rust:0.1.0 for Rust backends - ghcr.io/dfinity/icp-dev-env-motoko:0.1.0 for Motoko backends Eliminates: provision-darwin/linux.sh, pre-download-launcher.sh, actions/setup-node, cargo install candid-extractor, ICP_CLI_GITHUB_TOKEN, and macOS runners. Consistent with hello_world and who_am_i examples. Co-Authored-By: Claude Sonnet 4.6 --- ...keys-encrypted-notes-app-vetkd-example.yml | 101 +++--------------- 1 file changed, 15 insertions(+), 86 deletions(-) diff --git a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml index 9ea7c2655..30794aa47 100644 --- a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml +++ b/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml @@ -1,4 +1,5 @@ name: rust-vetkeys-encrypted-notes-app-vetkd + on: push: branches: @@ -6,98 +7,26 @@ on: pull_request: paths: - rust/vetkeys/encrypted_notes_app_vetkd/** - - .github/workflows/provision-darwin.sh - - .github/workflows/provision-linux.sh - .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml - - .ic-commit + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true -jobs: - rust-encrypted-notes-app-vetkd-darwin: - runs-on: macos-15 - steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: '22' - - name: Provision Darwin - run: bash .github/workflows/provision-darwin.sh - - name: Pre-download network launcher - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: bash .github/workflows/pre-download-launcher.sh - - name: Deploy (Rust backend) - env: - ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eExuo pipefail - pushd rust/vetkeys/encrypted_notes_app_vetkd/rust - icp network start -d && icp deploy - popd - motoko-encrypted-notes-app-vetkd-darwin: - runs-on: macos-15 - steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: '22' - - name: Provision Darwin - run: bash .github/workflows/provision-darwin.sh - - name: Pre-download network launcher - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: bash .github/workflows/pre-download-launcher.sh - - name: Deploy (Motoko backend) - env: - ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eExuo pipefail - pushd rust/vetkeys/encrypted_notes_app_vetkd/motoko - icp network start -d && icp deploy - popd - - rust-encrypted-notes-app-vetkd-linux: +jobs: + rust-encrypted-notes-app-vetkd: runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-rust:0.1.0 steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: '22' - - name: Provision Linux - run: bash .github/workflows/provision-linux.sh - - name: Pre-download network launcher - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: bash .github/workflows/pre-download-launcher.sh - - name: Deploy (Rust backend) - env: - ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eExuo pipefail - pushd rust/vetkeys/encrypted_notes_app_vetkd/rust - icp network start -d && icp deploy - popd - - motoko-encrypted-notes-app-vetkd-linux: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy Encrypted Notes App Vetkd Rust + working-directory: rust/vetkeys/encrypted_notes_app_vetkd/rust + run: icp network start -d && icp deploy + motoko-encrypted-notes-app-vetkd: runs-on: ubuntu-24.04 + container: ghcr.io/dfinity/icp-dev-env-motoko:0.1.0 steps: - - uses: actions/checkout@50fbc622fc4ef5163becd7fab6573eac35f8462e # v1.2.0 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: '22' - - name: Provision Linux - run: bash .github/workflows/provision-linux.sh - - name: Pre-download network launcher - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: bash .github/workflows/pre-download-launcher.sh - - name: Deploy (Motoko backend) - env: - ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -eExuo pipefail - pushd rust/vetkeys/encrypted_notes_app_vetkd/motoko - icp network start -d && icp deploy - popd + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + - name: Deploy Encrypted Notes App Vetkd Motoko + working-directory: rust/vetkeys/encrypted_notes_app_vetkd/motoko + run: icp network start -d && icp deploy From 9573929fbf80ceb63d495d61a25ee2391793c827 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 12:04:41 +0200 Subject: [PATCH 21/30] ci: rename workflow files to vetkeys-{example}.yml convention Follows hello_world/who_am_i naming (no language prefix, no -example suffix), keeping vetkeys- namespace prefix for grouping. Co-Authored-By: Claude Sonnet 4.6 --- ...-vetkd-example.yml => vetkeys-encrypted-notes-app-vetkd.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{rust-vetkeys-encrypted-notes-app-vetkd-example.yml => vetkeys-encrypted-notes-app-vetkd.yml} (93%) diff --git a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml similarity index 93% rename from .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml rename to .github/workflows/vetkeys-encrypted-notes-app-vetkd.yml index 30794aa47..9391d9c87 100644 --- a/.github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml +++ b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml @@ -7,7 +7,7 @@ on: pull_request: paths: - rust/vetkeys/encrypted_notes_app_vetkd/** - - .github/workflows/rust-vetkeys-encrypted-notes-app-vetkd-example.yml + - .github/workflows/vetkeys-encrypted-notes-app-vetkd.yml concurrency: group: ${{ github.workflow }}-${{ github.ref }} From b790b9ab5a3f6b862535524fe6d330ff9c3876a0 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 12:08:45 +0200 Subject: [PATCH 22/30] ci: fix workflow name and simplify job IDs to rust/motoko only Workflow name: vetkeys-{example} (no rust- prefix) Job IDs: rust and motoko (example name not needed in job context) Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/vetkeys-encrypted-notes-app-vetkd.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml index 9391d9c87..6aad1d027 100644 --- a/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml +++ b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml @@ -1,4 +1,4 @@ -name: rust-vetkeys-encrypted-notes-app-vetkd +name: vetkeys-encrypted-notes-app-vetkd on: push: @@ -14,7 +14,7 @@ concurrency: cancel-in-progress: true jobs: - rust-encrypted-notes-app-vetkd: + rust: runs-on: ubuntu-24.04 container: ghcr.io/dfinity/icp-dev-env-rust:0.1.0 steps: @@ -22,7 +22,7 @@ jobs: - name: Deploy Encrypted Notes App Vetkd Rust working-directory: rust/vetkeys/encrypted_notes_app_vetkd/rust run: icp network start -d && icp deploy - motoko-encrypted-notes-app-vetkd: + motoko: runs-on: ubuntu-24.04 container: ghcr.io/dfinity/icp-dev-env-motoko:0.1.0 steps: From ba85b4a1db7f8bc5fd521a5f558c221a4a982522 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 12:16:11 +0200 Subject: [PATCH 23/30] ci: pass ICP_CLI_GITHUB_TOKEN to avoid network launcher rate limiting The icp-dev-env container downloads the network launcher from GitHub on first run. Without authentication this hits the 60 req/hr unauthenticated API limit. Pass GITHUB_TOKEN so downloads use the authenticated 5000 req/hr limit. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/vetkeys-encrypted-notes-app-vetkd.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml index 6aad1d027..8751b25d9 100644 --- a/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml +++ b/.github/workflows/vetkeys-encrypted-notes-app-vetkd.yml @@ -17,6 +17,8 @@ jobs: rust: runs-on: ubuntu-24.04 container: ghcr.io/dfinity/icp-dev-env-rust:0.1.0 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Deploy Encrypted Notes App Vetkd Rust @@ -25,6 +27,8 @@ jobs: motoko: runs-on: ubuntu-24.04 container: ghcr.io/dfinity/icp-dev-env-motoko:0.1.0 + env: + ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 - name: Deploy Encrypted Notes App Vetkd Motoko From 9c7d7b08ccd203905887c1c2c434faf4366c31c3 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 13:18:20 +0200 Subject: [PATCH 24/30] fix: use LocalStorage only on localhost as workaround for IDB race condition Workaround for https://github.com/dfinity/icp-js-auth/issues/120 Co-Authored-By: Claude Sonnet 4.6 --- .../encrypted_notes_app_vetkd/frontend/src/store/auth.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts index ca1af79dd..30d513f59 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts @@ -1,6 +1,6 @@ import { get, writable } from "svelte/store"; import { type BackendActor, createActor } from "../lib/actor"; -import { AuthClient } from "@icp-sdk/auth/client"; +import { AuthClient, LocalStorage } from "@icp-sdk/auth/client"; import { CryptoService } from "../lib/crypto"; import { showError } from "./notifications"; import { navigateTo } from "svelte-router-spa"; @@ -19,8 +19,13 @@ async function initAuth() { const isLocal = window.location.hostname === "localhost" || window.location.hostname.endsWith(".localhost"); + // Workaround for https://github.com/dfinity/icp-js-auth/issues/120 + // IdbStorage has a race condition on localhost dev servers. LocalStorage + // avoids IDB on local but uses plain string storage (less secure), so + // production deployments keep the default secure IdbStorage + ECDSA key. const client = new AuthClient({ identityProvider: isLocal ? "http://id.ai.localhost:8000/authorize" : "https://id.ai/authorize", + ...(isLocal ? { storage: new LocalStorage(), keyType: "Ed25519" as const } : {}), }); if (client.isAuthenticated()) { authenticate(client); From a0a12382dec8f9a90e88c8c217d7f3922fc96a70 Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 13:34:55 +0200 Subject: [PATCH 25/30] fix: format LocalStorage spread for printWidth 80 Co-Authored-By: Claude Sonnet 4.6 --- .../frontend/src/store/auth.ts | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts index 30d513f59..a833f5062 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/store/auth.ts @@ -10,7 +10,12 @@ export type AuthState = | { state: "anonymous"; actor: BackendActor; client: AuthClient } | { state: "initializing-crypto"; actor: BackendActor; client: AuthClient } | { state: "synchronizing"; actor: BackendActor; client: AuthClient } - | { state: "initialized"; actor: BackendActor; client: AuthClient; crypto: CryptoService } + | { + state: "initialized"; + actor: BackendActor; + client: AuthClient; + crypto: CryptoService; + } | { state: "error"; error: string }; export const auth = writable({ state: "initializing-auth" }); @@ -24,8 +29,12 @@ async function initAuth() { // avoids IDB on local but uses plain string storage (less secure), so // production deployments keep the default secure IdbStorage + ECDSA key. const client = new AuthClient({ - identityProvider: isLocal ? "http://id.ai.localhost:8000/authorize" : "https://id.ai/authorize", - ...(isLocal ? { storage: new LocalStorage(), keyType: "Ed25519" as const } : {}), + identityProvider: isLocal + ? "http://id.ai.localhost:8000/authorize" + : "https://id.ai/authorize", + ...(isLocal + ? { storage: new LocalStorage(), keyType: "Ed25519" as const } + : {}), }); if (client.isAuthenticated()) { authenticate(client); @@ -96,13 +105,16 @@ export async function authenticate(client: AuthClient) { function handleSessionTimeout() { setTimeout(() => { try { - const delegation = JSON.parse(window.localStorage.getItem("ic-delegation") ?? "null") as { + const delegation = JSON.parse( + window.localStorage.getItem("ic-delegation") ?? "null", + ) as { delegations: Array<{ delegation: { expiration: string } }>; } | null; if (!delegation) return; const expirationTimeMs = - Number.parseInt(delegation.delegations[0].delegation.expiration, 16) / 1000000; + Number.parseInt(delegation.delegations[0].delegation.expiration, 16) / + 1000000; setTimeout(() => { logout(); From 0b8f1269ac1555ada14412e9614aa759d794067d Mon Sep 17 00:00:00 2001 From: Marco Walz Date: Wed, 3 Jun 2026 16:20:10 +0200 Subject: [PATCH 26/30] fix(encrypted_notes): add principal to auth state, fix getIdentity calls getIdentity() is async in @icp-sdk/auth@7.1.0. Capture identity once in authenticate(), derive principal, and propagate through all auth states (initializing-crypto, synchronizing, initialized). Replace all getIdentity().getPrincipal() calls in components with $auth.principal. Co-Authored-By: Claude Sonnet 4.6 --- .../frontend/src/components/EditNote.svelte | 2 +- .../frontend/src/components/NewNote.svelte | 2 +- .../frontend/src/components/SidebarLayout.svelte | 2 +- .../frontend/src/store/auth.ts | 12 +++++++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte index a5ef18c09..9f36b5468 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/EditNote.svelte @@ -85,7 +85,7 @@ if ($auth.state !== 'initialized') { throw new Error('expected the auth.state to be initialized'); } - return $auth.client.getIdentity().getPrincipal().toString(); + return $auth.principal.toString(); } $: { diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte index 5901ed075..31e71eda2 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/NewNote.svelte @@ -27,7 +27,7 @@ } creating = true; await addNote( - noteFromContent(DOMPurify.sanitize(editor.getHTML()), tags, $auth.client.getIdentity().getPrincipal()), + noteFromContent(DOMPurify.sanitize(editor.getHTML()), tags, $auth.principal), $auth.actor, $auth.crypto ) diff --git a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte index 1dcbd41f7..061789751 100644 --- a/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte +++ b/rust/vetkeys/encrypted_notes_app_vetkd/frontend/src/components/SidebarLayout.svelte @@ -27,7 +27,7 @@
My Principal:
-
{$auth.client.getIdentity().getPrincipal()}
+
{$auth.principal}