diff --git a/.github/workflows/check-doc-links.yml b/.github/workflows/check-doc-links.yml
new file mode 100644
index 00000000..1cd2c061
--- /dev/null
+++ b/.github/workflows/check-doc-links.yml
@@ -0,0 +1,31 @@
+name: Check Doc Links
+
+# Fails a PR when current docs (docs/) contain browser-relative internal links
+# that should be source-tree-relative .md / Card docId links. Run
+# `npm run normalize-doc-links` locally to fix. Scoped to docs/ only;
+# versioned_docs snapshots are healed at deploy time, not validated here.
+on:
+ pull_request:
+ paths:
+ - "docs/**"
+ - "scripts/normalize-doc-links.mjs"
+ - ".github/workflows/check-doc-links.yml"
+
+jobs:
+ check-doc-links:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: "npm"
+
+ - name: Install deps
+ run: npm install --frozen-lockfile
+
+ - name: Check doc links
+ run: npm run check:doc-links
diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml
index a74d12e0..407396ab 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -346,6 +346,12 @@ jobs:
- name: Install deps
run: npm install --frozen-lockfile
+ # Heal browser-relative internal links across the whole tree (current docs
+ # + every versioned_docs snapshot) before building. Versioned snapshots are
+ # not normalized in-repo, so this is where historical versions get fixed.
+ - name: Normalize docs links
+ run: npm run normalize-doc-links:all
+
- name: Build site
env:
NODE_OPTIONS: "--max-old-space-size=12288" # 12GB
diff --git a/docs/builder/get-started/accounts.md b/docs/builder/get-started/accounts.md
index eb12da6a..371d299b 100644
--- a/docs/builder/get-started/accounts.md
+++ b/docs/builder/get-started/accounts.md
@@ -108,7 +108,7 @@ To run the code examples in this guide, you'll need to set up a development envi
### Rust Environment
-If you already created `my-test-project` during [installation](./setup/installation#rust-project), you can reuse it. Otherwise, create a new project:
+If you already created `my-test-project` during [installation](./setup/installation.md#rust-project), you can reuse it. Otherwise, create a new project:
```bash title=">_ Terminal"
miden new my-project
@@ -129,7 +129,7 @@ cargo run --bin demo --release
### TypeScript Environment
-If you already created `miden-app` during [installation](./setup/installation#typescript-project), you can reuse it. Otherwise, scaffold a new Vite vanilla-ts project:
+If you already created `miden-app` during [installation](./setup/installation.md#typescript-project), you can reuse it. Otherwise, scaffold a new Vite vanilla-ts project:
```bash title=">_ Terminal"
npm create vite@latest miden-app -- --template vanilla-ts
@@ -160,7 +160,7 @@ npm run dev
Open the dev-server URL in the browser and watch the devtools console for output.
:::tip
-For detailed frontend setup guidance (React, wallets, UI), see the [Tutorials section](../tutorials/).
+For detailed frontend setup guidance (React, wallets, UI), see the [Tutorials section](../tutorials/index.md).
:::
## Creating Accounts Programmatically
@@ -418,6 +418,6 @@ Faucet account ID: 0xde0ba31282f7522046d3d4af40722b
- **TokenPolicyManager**: Configures mint, burn, send, and receive policies
- **AuthSingleSig**: Handles cryptographic authentication (Falcon512 or ECDSA via the `AuthScheme` enum)
-Now that you understand how to create accounts and faucets, you're ready to learn about Miden's unique transaction model. Continue to [Notes & Transactions](./notes) to explore how assets move between accounts using notes.
+Now that you understand how to create accounts and faucets, you're ready to learn about Miden's unique transaction model. Continue to [Notes & Transactions](./notes.md) to explore how assets move between accounts using notes.
---
diff --git a/docs/builder/get-started/index.md b/docs/builder/get-started/index.md
index 120fb9a1..e22bb0fb 100644
--- a/docs/builder/get-started/index.md
+++ b/docs/builder/get-started/index.md
@@ -25,22 +25,22 @@ Key concepts you'll encounter:
Follow these guides in order:
-
+
Install the Miden toolchain with `midenup`.
-
+
Essential Miden CLI commands — create a wallet and mint your first tokens.
-
+
Create and manage Miden accounts programmatically in Rust and TypeScript.
-
+
Miden's note-based transaction model for private asset transfers.
-
+
Query account storage data and interact with deployed smart contracts.
-
+
Build, test, and deploy a smart contract on Miden using Rust.
diff --git a/docs/builder/get-started/notes.md b/docs/builder/get-started/notes.md
index d61501ad..cc5dac06 100644
--- a/docs/builder/get-started/notes.md
+++ b/docs/builder/get-started/notes.md
@@ -57,7 +57,7 @@ This approach provides several advantages over direct transfers:
## Set Up Development Environment
-To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts#set-up-development-environment) guide.
+To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts.md#set-up-development-environment) guide.
## Minting Tokens
diff --git a/docs/builder/get-started/read-storage.md b/docs/builder/get-started/read-storage.md
index 198c9893..6a8afdf5 100644
--- a/docs/builder/get-started/read-storage.md
+++ b/docs/builder/get-started/read-storage.md
@@ -26,7 +26,7 @@ Miden accounts contain several types of data you can read.
## Set Up Development Environment
-To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts#set-up-development-environment) guide.
+To run the code examples in this guide, you'll need to set up a development environment. If you haven't already, follow the setup instructions in the [Accounts](./accounts.md#set-up-development-environment) guide.
## Reading from a Public Smart Contract
diff --git a/docs/builder/get-started/your-first-smart-contract/create.md b/docs/builder/get-started/your-first-smart-contract/create.md
index 9d5d2248..c47d2b19 100644
--- a/docs/builder/get-started/your-first-smart-contract/create.md
+++ b/docs/builder/get-started/your-first-smart-contract/create.md
@@ -314,6 +314,6 @@ The counter example demonstrates a complete interaction pattern: the account con
## Next Steps
-Now that you understand the contract code structure, let's move on to [deploying your contract](./deploy) and learn how the integration folder enables interaction with your contracts on the Miden network.
+Now that you understand the contract code structure, let's move on to [deploying your contract](./deploy.md) and learn how the integration folder enables interaction with your contracts on the Miden network.
---
diff --git a/docs/builder/get-started/your-first-smart-contract/index.md b/docs/builder/get-started/your-first-smart-contract/index.md
index 826cec03..e26c1115 100644
--- a/docs/builder/get-started/your-first-smart-contract/index.md
+++ b/docs/builder/get-started/your-first-smart-contract/index.md
@@ -31,13 +31,13 @@ The counter example is designed to teach core Miden concepts through a simple, u
## Prerequisites
-Before starting this guide, ensure you have completed the [Installation](../setup/installation) tutorial and have:
+Before starting this guide, ensure you have completed the [Installation](../setup/installation.md) tutorial and have:
- **Rust toolchain** installed and configured
- **midenup toolchain** installed with Miden CLI tools
:::tip Prerequisites Required
-You need those development tools installed for this guide. If you haven't set up your environment yet, please complete the [installation](../setup/installation) guide first.
+You need those development tools installed for this guide. If you haven't set up your environment yet, please complete the [installation](../setup/installation.md) guide first.
:::
## No Prior Experience Required
@@ -62,9 +62,9 @@ If you get stuck during this tutorial:
This tutorial is divided into focused sections:
-1. **[Create Your Project](./create)** - Set up your workspace and understand the counter contract code
-2. **[Deploy Your Contract](./deploy)** - Learn the integration folder and deploy to testnet
-3. **[Test Your Contract](./test)** - Learn how to effectively test your contracts
+1. **[Create Your Project](./create.md)** - Set up your workspace and understand the counter contract code
+2. **[Deploy Your Contract](./deploy.md)** - Learn the integration folder and deploy to testnet
+3. **[Test Your Contract](./test.md)** - Learn how to effectively test your contracts
Each section builds on the previous one, so we recommend following them in order.
diff --git a/docs/builder/get-started/your-first-smart-contract/test.md b/docs/builder/get-started/your-first-smart-contract/test.md
index 660e123f..047bf22e 100644
--- a/docs/builder/get-started/your-first-smart-contract/test.md
+++ b/docs/builder/get-started/your-first-smart-contract/test.md
@@ -303,7 +303,7 @@ Congratulations! You've successfully completed the Miden smart contract quick st
To deepen your knowledge, we recommend exploring the following resources:
-- Visit the [Tutorials section](../../tutorials/) for detailed, hands-on guides on topics such as contract interactions, advanced storage, custom note scripting, and integrating with external applications.
-- For in-depth technical explanations, consult the [Reference section](../../../reference/) of the documentation. Here you'll find comprehensive information on Miden's architecture, account model, transaction lifecycle, and the underlying zero-knowledge technology that powers the network.
+- Visit the [Tutorials section](../../tutorials/index.md) for detailed, hands-on guides on topics such as contract interactions, advanced storage, custom note scripting, and integrating with external applications.
+- For in-depth technical explanations, consult the [Reference section](../../../reference/index.md) of the documentation. Here you'll find comprehensive information on Miden's architecture, account model, transaction lifecycle, and the underlying zero-knowledge technology that powers the network.
The foundational patterns and concepts you've practiced in this Quick Start will enable you to build complex, privacy-preserving applications on the Miden network. Continue with the resources above to take your development further!
diff --git a/docs/builder/glossary.md b/docs/builder/glossary.md
index dc20cb0b..6e5e7fea 100644
--- a/docs/builder/glossary.md
+++ b/docs/builder/glossary.md
@@ -55,7 +55,7 @@ A key-value store associated with an account. Made up of storage slots.
### MultiSig
-A multi-signature account on Miden that requires a configurable threshold (N-of-M) of authorized signers to approve transactions before execution. MultiSig workflows are coordinated through [Miden Guardian](./miden-guardian/).
+A multi-signature account on Miden that requires a configurable threshold (N-of-M) of authorized signers to approve transactions before execution. MultiSig workflows are coordinated through [Miden Guardian](./miden-guardian/index.md).
## Notes & assets
@@ -125,11 +125,11 @@ A data structure that represents the basic unit of computation and storage in Mi
### Miden Guardian
-Infrastructure built by OpenZeppelin for managing private account state on Miden. Guardian provides a server and client SDKs for backing up, syncing, and coordinating state across devices and parties without trust assumptions. See the [Miden Guardian documentation](./miden-guardian/).
+Infrastructure built by OpenZeppelin for managing private account state on Miden. Guardian provides a server and client SDKs for backing up, syncing, and coordinating state across devices and parties without trust assumptions. See the [Miden Guardian documentation](./miden-guardian/index.md).
### Canonicalization
-The background process by which [Miden Guardian](./miden-guardian/) promotes candidate deltas to canonical status by verifying them against the Miden network.
+The background process by which [Miden Guardian](./miden-guardian/index.md) promotes candidate deltas to canonical status by verifying them against the Miden network.
### Delta
@@ -137,7 +137,7 @@ A Delta represents the changes between two states `s` and `s'`. Applying a Delta
### Delta Proposal
-A coordination mechanism in [Miden Guardian](./miden-guardian/) that allows multiple signers to propose, review, and co-sign state changes before they are promoted to a canonical delta.
+A coordination mechanism in [Miden Guardian](./miden-guardian/index.md) that allows multiple signers to propose, review, and co-sign state changes before they are promoted to a canonical delta.
### Threshold Signature
diff --git a/docs/builder/index.md b/docs/builder/index.md
index 619c5ec2..6331574b 100644
--- a/docs/builder/index.md
+++ b/docs/builder/index.md
@@ -8,36 +8,36 @@ pagination_next: null
Miden is a zero-knowledge layer 2 where every account is an independent state machine and state is private by default. You author accounts, notes, and transactions in Rust, compile them to Miden Assembly (MASM), and prove them client-side — the network verifies each proof without ever seeing your private state.
-This is the developer's entry point. **New to Miden?** Start with [Get started](./get-started) to install the toolchain and send your first transaction, then build [Your first smart contract](./get-started/your-first-smart-contract), and dip into the [Reference](../reference) when you want to know how it all works underneath. Already oriented? Jump straight to any section below.
+This is the developer's entry point. **New to Miden?** Start with [Get started](./get-started/index.md) to install the toolchain and send your first transaction, then build [Your first smart contract](./get-started/your-first-smart-contract/index.md), and dip into the [Reference](../reference/index.md) when you want to know how it all works underneath. Already oriented? Jump straight to any section below.
## Start here
-
+
Install midenup, create a wallet, and send your first transaction — in under ten minutes.
-
+
Walk through writing, proving, and deploying a counter contract in Rust.
-Building against the public testnet? Grab free test assets from the [faucet](https://faucet.testnet.miden.io/), and find every public endpoint — RPC, block explorer, status — on the [Network](./tools/network) page.
+Building against the public testnet? Grab free test assets from the [faucet](https://faucet.testnet.miden.io/), and find every public endpoint — RPC, block explorer, status — on the [Network](./tools/network.md) page.
## Build
-
+
Accounts, notes, storage, components, transactions — the full Rust SDK surface.
-
+
Real-world examples: the Miden Bank, private multisig, custom note scripts.
-
+
Testing, debugging, and common pitfalls when writing Miden programs.
-
+
Rust, Web, and React SDKs · playground · block explorer · CLI.
@@ -45,13 +45,13 @@ Building against the public testnet? Grab free test assets from the [faucet](htt
## Ship
-
+
Breaking changes, renames, and new features across accounts, notes, transactions, MASM, and the client.
-
+
Backup, sync, and coordinate private account state across devices.
-
+
Multi-party threshold signature workflows built on Miden.
@@ -59,10 +59,10 @@ Building against the public testnet? Grab free test assets from the [faucet](htt
## Reference
-
+
Frequently asked questions about Miden.
-
+
Key terms and definitions used throughout the docs.
diff --git a/docs/builder/miden-guardian/index.md b/docs/builder/miden-guardian/index.md
index 8eab808a..9ebe94b4 100644
--- a/docs/builder/miden-guardian/index.md
+++ b/docs/builder/miden-guardian/index.md
@@ -70,7 +70,7 @@ Guardian has an explicit trust boundary:
End-to-end Compose walkthroughs: signers, dashboard, observability, TLS.
-
+
Multi-party threshold signature workflows powered by Guardian.
diff --git a/docs/builder/migration/05-asset-vault-faucet.md b/docs/builder/migration/05-asset-vault-faucet.md
index 94bbfd18..11f5842b 100644
--- a/docs/builder/migration/05-asset-vault-faucet.md
+++ b/docs/builder/migration/05-asset-vault-faucet.md
@@ -43,7 +43,7 @@ let bal: AssetAmount = vault.get_balance(key)?;
### Summary
-The separate `BasicFungibleFaucet` and `NetworkFungibleFaucet` components were merged into a single **`FungibleFaucet`** component, and its old `FungibleFaucetBuilder` was replaced with a `bon`‑generated builder (`FungibleFaucet::builder()`). The constructor accepts a structured `TokenName` plus optional token‑metadata fields and an `AssetAmount` `max_supply`. A companion `FungibleTokenMetadata` component exposes the metadata via MASM getters. For the end‑to‑end client construction recipe (with `TokenPolicyManager`), see [(Rust) `FungibleFaucet` builder + `TokenPolicyManager`](./client-changes#rust-fungiblefaucet-builder--tokenpolicymanager-construction).
+The separate `BasicFungibleFaucet` and `NetworkFungibleFaucet` components were merged into a single **`FungibleFaucet`** component, and its old `FungibleFaucetBuilder` was replaced with a `bon`‑generated builder (`FungibleFaucet::builder()`). The constructor accepts a structured `TokenName` plus optional token‑metadata fields and an `AssetAmount` `max_supply`. A companion `FungibleTokenMetadata` component exposes the metadata via MASM getters. For the end‑to‑end client construction recipe (with `TokenPolicyManager`), see [(Rust) `FungibleFaucet` builder + `TokenPolicyManager`](./07-client-changes.md#rust-fungiblefaucet-builder--tokenpolicymanager-construction).
### Affected Code
@@ -73,7 +73,7 @@ let faucet = FungibleFaucet::builder()
A new **`AssetComposition`** enum (`None`, `Fungible`, `Custom`) discriminates assets, and the asset vault key's metadata byte now encodes the composition (plus the asset‑callback flag). `AssetVaultKey::new(asset_id, faucet_id, composition, callback_flag)` is the general constructor; `AssetVaultKey::new_fungible(faucet_id, callback_flag)` is the fungible shortcut. (`Custom` composition is reserved and currently rejected.)
-**What composition means:** it describes how two instances of the same asset combine in a vault — `None` (non‑fungible: instances never merge), `Fungible` (instances merge by summing amounts), and `Custom` (reserved for faucet‑defined logic; rejected at construction today). Because composition is carried in the key's metadata byte rather than derived from the faucet ID, the vault key is self‑describing. Read it back with `AssetVaultKey::composition()` and the callback flag with `AssetVaultKey::callback_flag()`. See the [asset encoding reference](../../reference/protocol/asset#encoding) and [composition reference](../../reference/protocol/asset#composition) for the full layout, and [MASM Changes](./masm-changes#asset-vault-key-composition) for the procedure‑level effects.
+**What composition means:** it describes how two instances of the same asset combine in a vault — `None` (non‑fungible: instances never merge), `Fungible` (instances merge by summing amounts), and `Custom` (reserved for faucet‑defined logic; rejected at construction today). Because composition is carried in the key's metadata byte rather than derived from the faucet ID, the vault key is self‑describing. Read it back with `AssetVaultKey::composition()` and the callback flag with `AssetVaultKey::callback_flag()`. See the [asset encoding reference](../../reference/protocol/asset#encoding) and [composition reference](../../reference/protocol/asset#composition) for the full layout, and [MASM Changes](./08-masm-changes.md#asset-vault-key-composition) for the procedure‑level effects.
### Migration Steps
diff --git a/docs/builder/migration/07-client-changes.md b/docs/builder/migration/07-client-changes.md
index 2db81422..0f0350a3 100644
--- a/docs/builder/migration/07-client-changes.md
+++ b/docs/builder/migration/07-client-changes.md
@@ -424,7 +424,7 @@ const proven2 = await wasmWebClient.proveTransaction(txResult);
## (Web) `InputNoteRecord.nullifier()` returns `string | undefined`
### Summary
-Because a 0.15 nullifier folds in the note's metadata (see [Nullifier now includes metadata and attachments commitment](./note-changes#nullifier-now-includes-metadata-and-attachments-commitment)), a **partial (metadata-less) input note record has no computable nullifier** — `InputNoteRecord.nullifier()` now returns `string | undefined`, pairing with `id()`'s `NoteId | undefined`. A record missing either is a partial note that sync has not yet completed.
+Because a 0.15 nullifier folds in the note's metadata (see [Nullifier now includes metadata and attachments commitment](./04-note-changes.md#nullifier-now-includes-metadata-and-attachments-commitment)), a **partial (metadata-less) input note record has no computable nullifier** — `InputNoteRecord.nullifier()` now returns `string | undefined`, pairing with `id()`'s `NoteId | undefined`. A record missing either is a partial note that sync has not yet completed.
### Migration Steps
1. Guard `record.nullifier()` against `undefined` alongside the existing `record.id()` guard; treat records missing either as not-yet-consumable and skip them from listings.
diff --git a/docs/builder/migration/08-masm-changes.md b/docs/builder/migration/08-masm-changes.md
index 5318e791..d858ee0a 100644
--- a/docs/builder/migration/08-masm-changes.md
+++ b/docs/builder/migration/08-masm-changes.md
@@ -85,7 +85,7 @@ eq.COMPOSITION_FUNGIBLE
2. To branch on the asset type, call `asset::key_to_composition` and compare against the `COMPOSITION_*` constants instead of inspecting raw bits.
3. No change is needed for `asset::key_to_faucet_id`, `asset::key_into_faucet_id`, `asset::key_to_asset_id`, or `asset::key_into_asset_id` — their names and stack effects are unchanged. Only code that **hand-decodes** the metadata byte is affected by the bit-layout shift; callers using these helper procs are not.
-See [Assets, Vault & Faucet](./asset-vault-faucet) for the matching Rust-side `AssetComposition` / `AssetVaultKey` changes.
+See [Assets, Vault & Faucet](./05-asset-vault-faucet.md) for the matching Rust-side `AssetComposition` / `AssetVaultKey` changes.
---
diff --git a/docs/builder/migration/index.md b/docs/builder/migration/index.md
index 95e1e0d5..97a56272 100644
--- a/docs/builder/migration/index.md
+++ b/docs/builder/migration/index.md
@@ -78,7 +78,7 @@ This guide is for:
- **Smart contract authors** writing MASM or using protocol APIs
- **App developers** using the protocol, standards, or client crates
-If you're starting fresh on v0.15, you can skip this guide and go directly to the [Get Started guide](../get-started).
+If you're starting fresh on v0.15, you can skip this guide and go directly to the [Get Started guide](../get-started/index.md).
:::
---
@@ -129,16 +129,16 @@ Work through these sections in order for a complete migration:
| Section | Topics |
|---------|--------|
-| [1. Imports & Dependencies](./imports-dependencies) | Crate bumps, package.json, MSRV 1.93, no round-trip of 0.14 artifacts |
-| [2. Hashing, SMT & Crypto Changes](./hashing-stack) | Poseidon2-domain-separated SMT leaves, `miden-crypto` 0.25 renames, `PartialSmt` / `LargeSmt` / 0.24 API breaks |
-| [3. Account Changes](./account-changes) | `AccountType` removed/renamed, network-account allowlist, `procedure_root!`, typed roots |
-| [4. Note Changes](./note-changes) | `NoteDetailsCommitment`, `PartialNoteMetadata`, multiple attachments, 1-bit `NoteType`, nullifier change, PSWAP |
-| [5. Assets, Vault & Faucet](./asset-vault-faucet) | `AssetAmount`, unified `FungibleFaucet`, `AssetVaultKey`, `AssetComposition` |
-| [6. Transaction Changes](./transaction-changes) | `fee_faucet_id`, `TransactionScriptRoot`, `ProvenBatch::new_unchecked` |
-| [7. Client Changes](./client-changes) | `GetAccount` surface, `sync_nullifiers`, `TokenPolicyManager`, Web/React/CLI changes |
-| [8. MASM Changes](./masm-changes) | `metadata_into_*` renames, trimmed kernel outputs, `adv_push.N` removed |
-| [9. VM & Assembler Changes](./vm-assembler) | Sync-first execution, `prove_sync`, stricter assembly, MAST wire format `0.0.3` |
-| [10. Rust SDK & Compiler Changes](./rust-sdk-compiler) | `#[component]` trait + storage struct, required `miden-project.toml`, explicit `#[account(...)]`, v0.15 tx-kernel bindings |
+| [1. Imports & Dependencies](./01-imports-dependencies.md) | Crate bumps, package.json, MSRV 1.93, no round-trip of 0.14 artifacts |
+| [2. Hashing, SMT & Crypto Changes](./02-hashing-stack.md) | Poseidon2-domain-separated SMT leaves, `miden-crypto` 0.25 renames, `PartialSmt` / `LargeSmt` / 0.24 API breaks |
+| [3. Account Changes](./03-account-changes.md) | `AccountType` removed/renamed, network-account allowlist, `procedure_root!`, typed roots |
+| [4. Note Changes](./04-note-changes.md) | `NoteDetailsCommitment`, `PartialNoteMetadata`, multiple attachments, 1-bit `NoteType`, nullifier change, PSWAP |
+| [5. Assets, Vault & Faucet](./05-asset-vault-faucet.md) | `AssetAmount`, unified `FungibleFaucet`, `AssetVaultKey`, `AssetComposition` |
+| [6. Transaction Changes](./06-transaction-changes.md) | `fee_faucet_id`, `TransactionScriptRoot`, `ProvenBatch::new_unchecked` |
+| [7. Client Changes](./07-client-changes.md) | `GetAccount` surface, `sync_nullifiers`, `TokenPolicyManager`, Web/React/CLI changes |
+| [8. MASM Changes](./08-masm-changes.md) | `metadata_into_*` renames, trimmed kernel outputs, `adv_push.N` removed |
+| [9. VM & Assembler Changes](./09-vm-assembler.md) | Sync-first execution, `prove_sync`, stricter assembly, MAST wire format `0.0.3` |
+| [10. Rust SDK & Compiler Changes](./10-rust-sdk-compiler.md) | `#[component]` trait + storage struct, required `miden-project.toml`, explicit `#[account(...)]`, v0.15 tx-kernel bindings |
---
diff --git a/docs/builder/private-multisig/index.md b/docs/builder/private-multisig/index.md
index f3666fc7..e1eda865 100644
--- a/docs/builder/private-multisig/index.md
+++ b/docs/builder/private-multisig/index.md
@@ -17,7 +17,7 @@ In Miden's private account model, account state lives client-side. The chain sto
- Proposals and signatures need an offchain coordination surface.
- Without a shared state view, participants risk divergent state or stale approvals.
-The [Miden Guardian](../miden-guardian/) solves this by acting as the coordination server for multisig accounts — keeping signers synchronized, managing proposal workflows, and ensuring all parties work from the same canonical state.
+The [Miden Guardian](../miden-guardian/index.md) solves this by acting as the coordination server for multisig accounts — keeping signers synchronized, managing proposal workflows, and ensuring all parties work from the same canonical state.
## How it works
@@ -57,13 +57,13 @@ sequenceDiagram
## Learn more
-
+
Transaction lifecycle, key architecture, and offline fallback.
-
+
Rust SDK for building multisig workflows.
-
+
TypeScript SDK for building multisig workflows.
diff --git a/docs/builder/smart-contracts/accounts/account-operations.md b/docs/builder/smart-contracts/accounts/account-operations.md
index 9285537e..c3c3a4b3 100644
--- a/docs/builder/smart-contracts/accounts/account-operations.md
+++ b/docs/builder/smart-contracts/accounts/account-operations.md
@@ -92,7 +92,7 @@ When proof generation fails:
3. No state changes occur
4. The client receives an error describing the failure
-The last row is enforced at end-of-execution by the VM kernel rather than mid-execution: a transaction that mutates no account state (storage, vault, or nonce) **and** consumes no notes is rejected. The Rust client also catches this case before submission as `TransactionRequestError::NoInputNotesNorAccountChange`. See [Empty Transaction](../../tutorials/helpers/pitfalls#empty-transaction-no-state-change-no-notes) for the recommended pattern.
+The last row is enforced at end-of-execution by the VM kernel rather than mid-execution: a transaction that mutates no account state (storage, vault, or nonce) **and** consumes no notes is rejected. The Rust client also catches this case before submission as `TransactionRequestError::NoInputNotesNorAccountChange`. See [Empty Transaction](../../tutorials/helpers/pitfalls.md#empty-transaction-no-state-change-no-notes) for the recommended pattern.
## Example: ManagedWallet
@@ -132,7 +132,7 @@ impl ManagedWallet for ManagedWalletStorage {
}
```
-To move assets out of an account, create [output notes](../notes/output-notes) with `output_note::add_asset`. For signature verification and nonce management, see [Authentication](./authentication).
+To move assets out of an account, create [output notes](../notes/output-notes.md) with `output_note::add_asset`. For signature verification and nonce management, see [Authentication](./authentication.md).
:::info API Reference
Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/)
diff --git a/docs/builder/smart-contracts/accounts/authentication.md b/docs/builder/smart-contracts/accounts/authentication.md
index 4be67af3..f26b8017 100644
--- a/docs/builder/smart-contracts/accounts/authentication.md
+++ b/docs/builder/smart-contracts/accounts/authentication.md
@@ -6,7 +6,7 @@ description: "Authentication component pattern and nonce management for Miden ac
# Authentication
-Miden uses digital signatures for transaction authentication. Because transactions execute on the client rather than onchain validators, the system needs a way to prove that a transaction was authorized by the account owner. Without authentication, anyone could construct a valid proof that transfers assets out of an account. The nonce prevents replay attacks — without it, a valid proof could be resubmitted to execute the same state change twice. For details on the cryptographic primitives, see [Cryptography](./cryptography).
+Miden uses digital signatures for transaction authentication. Because transactions execute on the client rather than onchain validators, the system needs a way to prove that a transaction was authorized by the account owner. Without authentication, anyone could construct a valid proof that transfers assets out of an account. The nonce prevents replay attacks — without it, a valid proof could be resubmitted to execute the same state change twice. For details on the cryptographic primitives, see [Cryptography](./cryptography.md).
v0.15 uses a single scheme-agnostic [`AuthSingleSig`](https://docs.rs/miden-standards/latest/miden_standards/account/auth/struct.AuthSingleSig.html) component for single-signature accounts. It takes an auth scheme identifier such as `Falcon512Poseidon2` or `EcdsaK256Keccak`. The native hash function is Poseidon2, and the Falcon-512 verifier MASM module is `miden::core::crypto::dsa::falcon512_poseidon2`.
@@ -25,7 +25,7 @@ During transaction execution the kernel invokes the `@auth_script`-annotated pro
2. Computes the transaction summary message: `hash([ACCOUNT_DELTA_COMMITMENT, INPUT_NOTES_COMMITMENT, OUTPUT_NOTES_COMMITMENT, [0, 0, ref_block_num, final_nonce]])`.
3. Requests the signature from the advice provider and verifies it with the scheme indicated by the stored scheme ID.
-If verification fails, proof generation fails and the transaction is rejected before reaching the network. The signature itself isn't passed as a function argument — it's provided through the **advice provider**, a mechanism that supplies auxiliary data to the VM during proof generation. See [Advice Provider](../transactions/advice-provider) for the full API.
+If verification fails, proof generation fails and the transaction is rejected before reaching the network. The signature itself isn't passed as a function argument — it's provided through the **advice provider**, a mechanism that supplies auxiliary data to the VM during proof generation. See [Advice Provider](../transactions/advice-provider.md) for the full API.
## Attaching `AuthSingleSig` to an account
@@ -90,7 +90,7 @@ The nonce prevents replay attacks — each transaction must use a unique nonce.
The nonce is committed into the transaction proof. If someone tries to replay a transaction, the nonce won't match the account's current nonce and verification will fail.
-Auth components are invoked automatically by the kernel — you do not call them directly from note scripts or [transaction scripts](../transactions/transaction-scripts). For access control and security patterns, see [Patterns](../patterns).
+Auth components are invoked automatically by the kernel — you do not call them directly from note scripts or [transaction scripts](../transactions/transaction-scripts.md). For access control and security patterns, see [Patterns](../patterns.md).
:::info API Reference
Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/), [`AuthSingleSig`](https://docs.rs/miden-standards/latest/miden_standards/account/auth/struct.AuthSingleSig.html), [`AuthScheme`](https://docs.rs/miden-protocol/latest/miden_protocol/account/auth/enum.AuthScheme.html)
@@ -98,6 +98,6 @@ Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/), [`Auth
## Related
-- [Cryptography](./cryptography) — Falcon-512 / Poseidon2 verification and hashing primitives
-- [Advice Provider](../transactions/advice-provider) — supplying auxiliary data during proof generation
-- [Patterns](../patterns) — access control, rate limiting, and anti-patterns
+- [Cryptography](./cryptography.md) — Falcon-512 / Poseidon2 verification and hashing primitives
+- [Advice Provider](../transactions/advice-provider.md) — supplying auxiliary data during proof generation
+- [Patterns](../patterns.md) — access control, rate limiting, and anti-patterns
diff --git a/docs/builder/smart-contracts/accounts/components.md b/docs/builder/smart-contracts/accounts/components.md
index 01c5b9db..38aa8ef3 100644
--- a/docs/builder/smart-contracts/accounts/components.md
+++ b/docs/builder/smart-contracts/accounts/components.md
@@ -6,7 +6,7 @@ description: "Define Miden account components using the #[component] macro — s
# Components
-Components are the building blocks of Miden accounts. Each component defines a [storage](./storage) layout, exposes public methods, and can be composed with other components on the same account — for example, a wallet component + an auth component + custom logic. This modularity lets you reuse a wallet component across many accounts and test or upgrade components independently.
+Components are the building blocks of Miden accounts. Each component defines a [storage](./storage.md) layout, exposes public methods, and can be composed with other components on the same account — for example, a wallet component + an auth component + custom logic. This modularity lets you reuse a wallet component across many accounts and test or upgrade components independently.
## The `#[component]` macro
@@ -152,7 +152,7 @@ impl MyContract for MyContractStorage {
### Supported parameter and return types
-Public methods can use SDK types (`Felt`, `Word`, `Asset`, `AccountId`, `NoteIdx`) and custom types annotated with [`#[export_type]`](./custom-types).
+Public methods can use SDK types (`Felt`, `Word`, `Asset`, `AccountId`, `NoteIdx`) and custom types annotated with [`#[export_type]`](./custom-types.md).
## Auto-generated methods
@@ -199,7 +199,7 @@ self.compute_storage_commitment() -> Word
// ... and more (see API Reference)
```
-For the full list of auto-generated methods, see [Account Operations](./account-operations). To export your own types for use in public method signatures, see [Custom Types](./custom-types).
+For the full list of auto-generated methods, see [Account Operations](./account-operations.md). To export your own types for use in public method signatures, see [Custom Types](./custom-types.md).
:::info API Reference
Full API docs on docs.rs: [`miden`](https://docs.rs/miden/latest/miden/) (top-level — `#[component]` macro)
diff --git a/docs/builder/smart-contracts/accounts/cryptography.md b/docs/builder/smart-contracts/accounts/cryptography.md
index e243a36a..2550be9b 100644
--- a/docs/builder/smart-contracts/accounts/cryptography.md
+++ b/docs/builder/smart-contracts/accounts/cryptography.md
@@ -58,5 +58,5 @@ let hash: [u8; 32] = sha256_hash(input_bytes);
## Related
-- [Authentication](./authentication) — auth component pattern and nonce management
-- [Advice Provider](../transactions/advice-provider) — supplying auxiliary data during proof generation
+- [Authentication](./authentication.md) — auth component pattern and nonce management
+- [Advice Provider](../transactions/advice-provider.md) — supplying auxiliary data during proof generation
diff --git a/docs/builder/smart-contracts/accounts/custom-types.md b/docs/builder/smart-contracts/accounts/custom-types.md
index a827fb0e..77ad5aef 100644
--- a/docs/builder/smart-contracts/accounts/custom-types.md
+++ b/docs/builder/smart-contracts/accounts/custom-types.md
@@ -6,7 +6,7 @@ description: "Export custom structs and enums for use in public component method
# Custom Types
-When public [component](./components) methods use custom structs or enums, those types must be annotated with `#[export_type]` so the compiler can include them in the component's public API. Types used only internally (in private methods or local variables) don't need this annotation.
+When public [component](./components.md) methods use custom structs or enums, those types must be annotated with `#[export_type]` so the compiler can include them in the component's public API. Types used only internally (in private methods or local variables) don't need this annotation.
:::tip
If you forget `#[export_type]` on a public API type, the compiler will emit an error telling you to add it.
diff --git a/docs/builder/smart-contracts/accounts/storage.md b/docs/builder/smart-contracts/accounts/storage.md
index eb261ca6..cc90202f 100644
--- a/docs/builder/smart-contracts/accounts/storage.md
+++ b/docs/builder/smart-contracts/accounts/storage.md
@@ -176,7 +176,7 @@ let initial: Word = storage::get_initial_map_item(slot_id, &key);
These functions return values from before any modifications in the current transaction.
-For Felt and Word conversion details, see [Types](../types). To export your own types for public APIs, see [Custom Types](./custom-types). For common storage patterns like access control and rate limiting, see [Patterns](../patterns).
+For Felt and Word conversion details, see [Types](../types.md). To export your own types for public APIs, see [Custom Types](./custom-types.md). For common storage patterns like access control and rate limiting, see [Patterns](../patterns.md).
:::info API Reference
Full API docs on docs.rs: [`miden::storage`](https://docs.rs/miden/latest/miden/storage/)
diff --git a/docs/builder/smart-contracts/cross-component-calls.md b/docs/builder/smart-contracts/cross-component-calls.md
index 1a49e8be..463953f0 100644
--- a/docs/builder/smart-contracts/cross-component-calls.md
+++ b/docs/builder/smart-contracts/cross-component-calls.md
@@ -6,7 +6,7 @@ description: "Call methods across account components and from note scripts."
# Cross-Component Calls
-Miden [components](./accounts/components) can call each other's methods. Since accounts can have multiple components (e.g., wallet + auth + custom logic), those components need to communicate. [Note scripts](./notes/note-scripts) also need to call methods on the account's components to transfer assets.
+Miden [components](./accounts/components.md) can call each other's methods. Since accounts can have multiple components (e.g., wallet + auth + custom logic), those components need to communicate. [Note scripts](./notes/note-scripts.md) also need to call methods on the account's components to transfer assets.
## How it works
diff --git a/docs/builder/smart-contracts/index.md b/docs/builder/smart-contracts/index.md
index 6167899b..0b43f3b8 100644
--- a/docs/builder/smart-contracts/index.md
+++ b/docs/builder/smart-contracts/index.md
@@ -6,12 +6,12 @@ pagination_prev: null
# Miden Smart Contracts
-This section covers the developer-facing paths for building smart contracts on Miden: an authoring guide for **Miden Assembly (MASM)** (the supported path for mainnet production today) and **Rust** (in active development as the long-term direction), plus the [Miden Standards](./standards/) library of reusable components callable from either.
+This section covers the developer-facing paths for building smart contracts on Miden: an authoring guide for **Miden Assembly (MASM)** (the supported path for mainnet production today) and **Rust** (in active development as the long-term direction), plus the [Miden Standards](./standards/index.md) library of reusable components callable from either.
These pages track protocol v0.15.3, Miden VM / Assembly v0.23, miden-crypto v0.25, and the Rust smart-contract SDK macro surface from Miden SDK v0.13.
:::tip Building for mainnet?
-Miden mainnet supports smart contracts authored in **Miden Assembly (MASM)** today. The Rust SDK is in active development and will become the default authoring path once it ships v1. For production deployments now, see [MASM Smart Contracts](./masm/).
+Miden mainnet supports smart contracts authored in **Miden Assembly (MASM)** today. The Rust SDK is in active development and will become the default authoring path once it ships v1. For production deployments now, see [MASM Smart Contracts](./masm/index.md).
:::
If you're new to Miden, the hands-on [Miden Bank Tutorial](../tutorials/miden-bank/) walks through the full lifecycle using the Rust SDK; the concepts (accounts, notes, transactions, components) translate directly to MASM.
@@ -19,16 +19,16 @@ If you're new to Miden, the hands-on [Miden Bank Tutorial](../tutorials/miden-ba
## Sections
-
+
How accounts, notes, transactions, and components fit together. Concepts apply regardless of authoring language.
-
+
Author production-ready smart contracts directly in Miden Assembly. The path Miden mainnet supports today.
-
+
Build accounts, notes, transactions, and reusable logic with the Rust-first workflow. Currently in active development and not yet production-ready for mainnet.
-
+
Standard components, note scripts, faucet policies, and MASM modules. Callable from MASM or Rust.
@@ -45,13 +45,13 @@ If you're new to Miden, the hands-on [Miden Bank Tutorial](../tutorials/miden-ba
Transaction context, scripts, and the advice provider.
-
+
Calling methods across account components and from note scripts.
-
+
Core types: Felt, Word, AccountId, NoteId, and more.
-
+
Access control, rate limiting, spending limits, and anti-patterns.
diff --git a/docs/builder/smart-contracts/masm/index.md b/docs/builder/smart-contracts/masm/index.md
index f241f973..cfa34ac8 100644
--- a/docs/builder/smart-contracts/masm/index.md
+++ b/docs/builder/smart-contracts/masm/index.md
@@ -21,10 +21,10 @@ You're here because you want to deploy a contract to Miden mainnet. MASM is a sm
| **Mainnet production** | Supported today | In active development |
| **Performance / cycle control** | Direct control over emitted instructions | Compiles via Wasm → MASM; less predictable |
| **Learning curve** | Small instruction set, explicit stack semantics | Familiar Rust ergonomics |
-| **Miden Standards** | [Standards modules](../standards/) callable directly | Same standards via Rust bindings |
+| **Miden Standards** | [Standards modules](../standards/index.md) callable directly | Same standards via Rust bindings |
| **Long-term direction** | Long-lived for system-level / performance-critical code | Will become the default authoring path once mature |
-For mainnet shipping today, the choice is straightforward: write MASM. Most production patterns — account components, note scripts, transaction scripts, P2ID and P2IDE flows, faucet policies — are already covered by [Miden Standards](../standards/), so most contracts compose existing standards rather than rolling everything from scratch.
+For mainnet shipping today, the choice is straightforward: write MASM. Most production patterns — account components, note scripts, transaction scripts, P2ID and P2IDE flows, faucet policies — are already covered by [Miden Standards](../standards/index.md), so most contracts compose existing standards rather than rolling everything from scratch.
## Where the language reference lives
@@ -47,7 +47,7 @@ In the meantime, the existing [Rust-based Miden Bank tutorial](../../tutorials/m
## See also
-- [Miden Standards](../standards/) — reusable account components, standard notes, faucet policies, callable from MASM.
+- [Miden Standards](../standards/index.md) — reusable account components, standard notes, faucet policies, callable from MASM.
- [Reference → Miden VM → Assembly](/reference/miden-vm/user_docs/assembly/) — full language reference (instructions, stack semantics, control flow).
-- [Smart Contracts → Overview](../overview) — execution model and lifecycle (the concepts apply regardless of authoring language).
-- [Tools → Playground](../../tools/playground) — interactive browser-based MASM environment for quick experiments.
+- [Smart Contracts → Overview](../overview.md) — execution model and lifecycle (the concepts apply regardless of authoring language).
+- [Tools → Playground](../../tools/playground.md) — interactive browser-based MASM environment for quick experiments.
diff --git a/docs/builder/smart-contracts/notes/introduction.md b/docs/builder/smart-contracts/notes/introduction.md
index b5c11329..18a935ed 100644
--- a/docs/builder/smart-contracts/notes/introduction.md
+++ b/docs/builder/smart-contracts/notes/introduction.md
@@ -23,13 +23,13 @@ Every note has four parts:
| **Storage** | Custom data stored with the note that the script can read at consumption time (e.g., a target account ID, an expiration block) |
| **Metadata** | Sender ID, note tag (for discovery routing), and auxiliary data |
-The **recipient** is a Poseidon2 hash that encodes who can consume the note. When creating notes programmatically (via [`output_note::create`](./output-notes#create-a-note)), you compute a `Recipient` from the note's serial number, script root, and storage commitment:
+The **recipient** is a Poseidon2 hash that encodes who can consume the note. When creating notes programmatically (via [`output_note::create`](./output-notes.md#create-a-note)), you compute a `Recipient` from the note's serial number, script root, and storage commitment:
```
recipient = hash(hash(hash(serial_num, [0;4]), script_root), storage_commitment)
```
-Only someone who knows these values can construct a valid consumption proof. See [Computing a Recipient](./output-notes#computing-a-recipient) for the protocol helpers.
+Only someone who knows these values can construct a valid consumption proof. See [Computing a Recipient](./output-notes.md#computing-a-recipient) for the protocol helpers.
## The two-transaction model
@@ -63,7 +63,7 @@ Notes come in two visibility modes:
Private notes provide stronger privacy guarantees — the network can't even see what assets a note carries — but they require the sender and recipient to have a communication channel outside the protocol.
-Miden provides built-in note patterns (P2ID, P2IDE, SWAP) for common transfer scenarios — see [Standard Note Types](./note-types). You can also write fully custom note scripts for arbitrary consumption logic.
+Miden provides built-in note patterns (P2ID, P2IDE, SWAP) for common transfer scenarios — see [Standard Note Types](./note-types.md). You can also write fully custom note scripts for arbitrary consumption logic.
## How notes differ from EVM transfers
diff --git a/docs/builder/smart-contracts/notes/note-scripts.md b/docs/builder/smart-contracts/notes/note-scripts.md
index 159dc862..d2ef4b5a 100644
--- a/docs/builder/smart-contracts/notes/note-scripts.md
+++ b/docs/builder/smart-contracts/notes/note-scripts.md
@@ -91,7 +91,7 @@ pub fn run(self, _arg: Word, account: &mut Wallet) {
}
```
-Declare the account wrapper with `#[account(package::Interface)]` and point `miden-project.toml` at the dependency's generated WIT — see [Cross-Component Calls](../cross-component-calls).
+Declare the account wrapper with `#[account(package::Interface)]` and point `miden-project.toml` at the dependency's generated WIT — see [Cross-Component Calls](../cross-component-calls.md).
### Without account access
@@ -135,7 +135,7 @@ impl CounterNote {
}
```
-This note takes the active account as `&mut CounterAccount` and calls the counter component through its generated interface. See [Cross-Component Calls](../cross-component-calls).
+This note takes the active account as `&mut CounterAccount` and calls the counter component through its generated interface. See [Cross-Component Calls](../cross-component-calls.md).
## miden-project.toml for note scripts
@@ -161,5 +161,5 @@ counter-account = { wit = "../counter-account/target/generated-wit/" }
## Related
-- [Cross-Component Calls](../cross-component-calls) — how `#[account(...)]` wrappers and generated interfaces work
-- [Transaction Context](../transactions/transaction-context) — transaction scripts with `#[tx_script]`
+- [Cross-Component Calls](../cross-component-calls.md) — how `#[account(...)]` wrappers and generated interfaces work
+- [Transaction Context](../transactions/transaction-context.md) — transaction scripts with `#[tx_script]`
diff --git a/docs/builder/smart-contracts/notes/note-types.md b/docs/builder/smart-contracts/notes/note-types.md
index ceb06199..9dfec7d0 100644
--- a/docs/builder/smart-contracts/notes/note-types.md
+++ b/docs/builder/smart-contracts/notes/note-types.md
@@ -157,8 +157,8 @@ SwapNote::create(
Returns a tuple of `(Note, NoteDetails)` — the SWAP note to submit and the expected payback note details (for tracking).
-`NoteAttachments` is defined in `miden-protocol`. Use `NoteAttachments::empty()` when the note does not need auxiliary data — see [note attachments](./output-notes#note-attachments) for the underlying SDK API.
+`NoteAttachments` is defined in `miden-protocol`. Use `NoteAttachments::empty()` when the note does not need auxiliary data — see [note attachments](./output-notes.md#note-attachments) for the underlying SDK API.
## More note types
-For writing custom note scripts, see [Note Scripts](./note-scripts). For the transaction context and `#[tx_script]`, see [Transaction Context](../transactions/transaction-context).
+For writing custom note scripts, see [Note Scripts](./note-scripts.md). For the transaction context and `#[tx_script]`, see [Transaction Context](../transactions/transaction-context.md).
diff --git a/docs/builder/smart-contracts/notes/output-notes.md b/docs/builder/smart-contracts/notes/output-notes.md
index 36f40112..4578417b 100644
--- a/docs/builder/smart-contracts/notes/output-notes.md
+++ b/docs/builder/smart-contracts/notes/output-notes.md
@@ -54,7 +54,7 @@ Returns note metadata:
let metadata: NoteMetadata = output_note::get_metadata(note_idx);
```
-On the v0.15 protocol side, `NoteMetadata` combines `PartialNoteMetadata` (sender, note type, tag) with attachment headers and the attachments commitment. See [Reading Notes — Note metadata](./reading-notes#note-metadata) for details.
+On the v0.15 protocol side, `NoteMetadata` combines `PartialNoteMetadata` (sender, note type, tag) with attachment headers and the attachments commitment. See [Reading Notes — Note metadata](./reading-notes.md#note-metadata) for details.
## Note attachments
diff --git a/docs/builder/smart-contracts/notes/reading-notes.md b/docs/builder/smart-contracts/notes/reading-notes.md
index 657fbda7..1531a167 100644
--- a/docs/builder/smart-contracts/notes/reading-notes.md
+++ b/docs/builder/smart-contracts/notes/reading-notes.md
@@ -30,7 +30,7 @@ struct MyNote {
}
```
-See [Note Scripts](./note-scripts) for the full `#[note]` pattern. The low-level `active_note::get_storage()` function is also available for advanced use cases:
+See [Note Scripts](./note-scripts.md) for the full `#[note]` pattern. The low-level `active_note::get_storage()` function is also available for advanced use cases:
```rust
let storage: Vec = active_note::get_storage();
diff --git a/docs/builder/smart-contracts/overview.md b/docs/builder/smart-contracts/overview.md
index 32a745d4..cb8bf72a 100644
--- a/docs/builder/smart-contracts/overview.md
+++ b/docs/builder/smart-contracts/overview.md
@@ -7,7 +7,7 @@ description: "Miden's execution model, account structure, note system, and trans
# What is a Miden Smart Contract
:::info Concepts apply to both authoring paths
-This page describes Miden's execution model — accounts, notes, transactions, and lifecycle. The concepts apply regardless of whether you author contracts in MASM (the mainnet path) or the Rust SDK (in active development). Code examples on this page use Rust; for the MASM path, see [MASM Smart Contracts](./masm/).
+This page describes Miden's execution model — accounts, notes, transactions, and lifecycle. The concepts apply regardless of whether you author contracts in MASM (the mainnet path) or the Rust SDK (in active development). Code examples on this page use Rust; for the MASM path, see [MASM Smart Contracts](./masm/index.md).
:::
Miden is a zero-knowledge layer 2 where transactions execute on the client and only a cryptographic proof is submitted to the network. Every entity — wallets, contracts, faucets — is an account with code, storage, a vault, and a nonce. Assets move between accounts through notes, which act as programmable UTXOs. This page describes the execution model, account structure, note system, and transaction lifecycle. For a hands-on walkthrough, see the [Miden Bank Tutorial](../tutorials/miden-bank/).
@@ -33,7 +33,7 @@ Miden runs **MASM** (Miden Assembly) — the VM's native instruction set. There
MASM → ZK Circuit → Proof
```
-You write MASM directly and build it into a `.masp` package (Miden Assembly Package). This is what mainnet supports for production today. See [MASM Smart Contracts](./masm/).
+You write MASM directly and build it into a `.masp` package (Miden Assembly Package). This is what mainnet supports for production today. See [MASM Smart Contracts](./masm/index.md).
**Rust** (in active development):
@@ -41,7 +41,7 @@ You write MASM directly and build it into a `.masp` package (Miden Assembly Pack
Rust → Wasm → MASM → ZK Circuit → Proof
```
-The Miden compiler (`cargo-miden`) compiles your `#![no_std]` Rust to WebAssembly, then translates it to MASM. The output is the same `.masp` package, so both paths share the same execution model and the same [Miden Standards](./standards/) library.
+The Miden compiler (`cargo-miden`) compiles your `#![no_std]` Rust to WebAssembly, then translates it to MASM. The output is the same `.masp` package, so both paths share the same execution model and the same [Miden Standards](./standards/index.md) library.
When a transaction executes, the Miden VM runs the MASM and produces a zero-knowledge proof of correct execution. The output of `cargo miden build` (Rust) or the MASM build tooling is a `.masp` file containing the compiled MASM and metadata.
@@ -83,7 +83,7 @@ impl MyWallet for MyWalletStorage {
Each component defines its own storage layout and public methods. In the Rust SDK, storage lives on a `#[component_storage]` struct, the public API is a `#[component]` trait, and the `#[component] impl` block provides the behavior. The macro generates the necessary WIT (WebAssembly Interface Type) definitions for cross-component interoperability.
-See [Components](./accounts/components) for full details.
+See [Components](./accounts/components.md) for full details.
## Notes as UTXOs
@@ -142,7 +142,7 @@ If the assertion fails, the ZK circuit **cannot produce a valid proof**. This me
This is fundamentally different from Ethereum's `revert` — there's no onchain transaction that fails. The proof simply doesn't exist if the execution is invalid.
-A separate failure mode is the [empty transaction](../tutorials/helpers/pitfalls#empty-transaction-no-state-change-no-notes) — a transaction that completes without mutating account state or consuming a note is also rejected, since Miden refuses to admit transactions with no observable effect.
+A separate failure mode is the [empty transaction](../tutorials/helpers/pitfalls.md#empty-transaction-no-state-change-no-notes) — a transaction that completes without mutating account state or consuming a note is also rejected, since Miden refuses to admit transactions with no observable effect.
## Account types
@@ -161,23 +161,23 @@ Wallet, contract, and faucet behavior comes from the components and configuratio
| Section | When to use |
|---|---|
-| [MASM Smart Contracts](./masm/) | Production contracts for mainnet today |
-| [Rust SDK](./rust/) | Prototyping today; long-term default once it ships v1 |
-| [Miden Standards](./standards/) | Reusable building blocks callable from either path |
+| [MASM Smart Contracts](./masm/index.md) | Production contracts for mainnet today |
+| [Rust SDK](./rust/index.md) | Prototyping today; long-term default once it ships v1 |
+| [Miden Standards](./standards/index.md) | Reusable building blocks callable from either path |
**Topic guides** (concepts apply regardless of authoring language):
| Topic | Description |
|---|---|
-| [Components](./accounts/components) | Reusable code modules with storage and exported interfaces |
-| [Storage](./accounts/storage) | Up to 255 slots of `Value` or `StorageMap` |
-| [Custom Types](./accounts/custom-types) | Exported structs/enums for public APIs |
-| [Account Operations](./accounts/account-operations) | Read/write account state and vault |
+| [Components](./accounts/components.md) | Reusable code modules with storage and exported interfaces |
+| [Storage](./accounts/storage.md) | Up to 255 slots of `Value` or `StorageMap` |
+| [Custom Types](./accounts/custom-types.md) | Exported structs/enums for public APIs |
+| [Account Operations](./accounts/account-operations.md) | Read/write account state and vault |
| [Notes](./notes/) | Programmable UTXOs for asset transfers |
| [Transactions](./transactions/) | Transaction context, scripts, and the advice provider |
-| [Authentication](./accounts/authentication) | Falcon-512 Poseidon2 signatures and replay protection |
-| [Cross-Component Calls](./cross-component-calls) | Inter-component communication |
-| [Types](./types) | Felt, Word, Asset — the VM's native types |
-| [Patterns](./patterns) | Access control, rate limiting, spending limits, anti-patterns |
+| [Authentication](./accounts/authentication.md) | Falcon-512 Poseidon2 signatures and replay protection |
+| [Cross-Component Calls](./cross-component-calls.md) | Inter-component communication |
+| [Types](./types.md) | Felt, Word, Asset — the VM's native types |
+| [Patterns](./patterns.md) | Access control, rate limiting, spending limits, anti-patterns |
Ready to start building? The [Miden Bank Tutorial](../tutorials/miden-bank/) is a hands-on walkthrough (currently written against the Rust SDK; the concepts translate to MASM).
diff --git a/docs/builder/smart-contracts/patterns.md b/docs/builder/smart-contracts/patterns.md
index c68240d1..5ceba84c 100644
--- a/docs/builder/smart-contracts/patterns.md
+++ b/docs/builder/smart-contracts/patterns.md
@@ -16,7 +16,7 @@ Unlike Solidity, account component procedures cannot check "who is calling me."
- **Account components** rely on authentication components (Falcon512, ECDSA) which the transaction kernel invokes automatically in the epilogue
:::
-For account-level access control, Miden uses **authentication components** rather than manual sender checks. The transaction kernel calls the account's `auth` procedure automatically during the transaction epilogue — if the signature is invalid, the entire transaction fails. See [Authentication](./accounts/authentication) for the full pattern.
+For account-level access control, Miden uses **authentication components** rather than manual sender checks. The transaction kernel calls the account's `auth` procedure automatically during the transaction epilogue — if the signature is invalid, the entire transaction fails. See [Authentication](./accounts/authentication.md) for the full pattern.
For note-level access control, note scripts can check who created the note using `active_note::get_sender()`. The protocol-level `ownable` standard (`miden-standards/asm/standards/access/ownable.masm`) provides `verify_owner`, `get_owner`, `transfer_ownership`, and `renounce_ownership` procedures.
@@ -24,7 +24,7 @@ For note-level access control, note scripts can check who created the note using
Use `tx::get_block_number()` to enforce cooldown periods between actions. Store the last action block number in a `Value` storage slot, then compare against the current block number before allowing the next action.
-See [Transaction Context](./transactions/transaction-context) for the available block and transaction info functions.
+See [Transaction Context](./transactions/transaction-context.md) for the available block and transaction info functions.
## Security
@@ -41,7 +41,7 @@ When an assertion fails, proof generation fails and the transaction is rejected
### Replay protection
-Every state-changing transaction must increment the nonce. The auth component handles this automatically — see [Authentication](./accounts/authentication).
+Every state-changing transaction must increment the nonce. The auth component handles this automatically — see [Authentication](./accounts/authentication.md).
### Safe arithmetic
@@ -55,7 +55,7 @@ let elapsed = current_block.saturating_sub(last_block);
let elapsed = current_block - last_block;
```
-For Felt arithmetic, values wrap modulo the prime field (no overflow panic), but the result may not be what you expect if you're treating Felts as integers. See [Types — Felt](./types#felt--field-elements) for details.
+For Felt arithmetic, values wrap modulo the prime field (no overflow panic), but the result may not be what you expect if you're treating Felts as integers. See [Types — Felt](./types.md#felt--field-elements) for details.
### Anti-patterns
diff --git a/docs/builder/smart-contracts/rust/index.md b/docs/builder/smart-contracts/rust/index.md
index 53b140c7..13088329 100644
--- a/docs/builder/smart-contracts/rust/index.md
+++ b/docs/builder/smart-contracts/rust/index.md
@@ -10,7 +10,7 @@ The Rust SDK is the long-term direction for Miden smart-contract development: de
The examples in this section assume protocol v0.15.3, Miden VM / Assembly v0.23, miden-crypto v0.25, and the Miden SDK v0.13 macro surface.
:::caution Currently in active development
-The Rust SDK is being actively developed and is **not yet production-ready for mainnet**. For production deployments today, write contracts in [Miden Assembly (MASM)](../masm/) — the supported path Miden mainnet verifies. Use the Rust SDK for prototyping, experimentation, and exploration of the long-term direction.
+The Rust SDK is being actively developed and is **not yet production-ready for mainnet**. For production deployments today, write contracts in [Miden Assembly (MASM)](../masm/index.md) — the supported path Miden mainnet verifies. Use the Rust SDK for prototyping, experimentation, and exploration of the long-term direction.
:::
The pages below describe the Rust SDK's current shape: how accounts and components compose, how notes are scripted, how transactions execute, and the patterns you can already build with.
@@ -27,13 +27,13 @@ The pages below describe the Rust SDK's current shape: how accounts and componen
Transaction context, scripts, and the advice provider.
-
+
Calling methods across account components and from note scripts.
-
+
Core types: Felt, Word, AccountId, NoteId, and more.
-
+
Access control, rate limiting, spending limits, and anti-patterns.
@@ -42,16 +42,16 @@ The pages below describe the Rust SDK's current shape: how accounts and componen
| Concern | Rust SDK | MASM |
|---|---|---|
-| **Mainnet production** | In active development | [Supported today](../masm/) |
+| **Mainnet production** | In active development | [Supported today](../masm/index.md) |
| **Ergonomics** | Familiar Rust idioms, type-checked storage, attribute macros | Stack-based assembly, explicit control |
| **Compilation target** | Compiles via Wasm → MASM | Directly authored |
| **Use case** | Long-term default once mature; prototyping and exploration today | Production contracts for mainnet |
-Both authoring paths share the same [Miden Standards](../standards/) library — standard components, notes, and faucet policies are callable from either.
+Both authoring paths share the same [Miden Standards](../standards/index.md) library — standard components, notes, and faucet policies are callable from either.
## Related pages
-- [MASM Smart Contracts](../masm/) — the path mainnet supports today.
-- [Miden Standards](../standards/) — reusable building blocks callable from Rust or MASM.
-- [Smart Contracts → Overview](../overview) — execution model and lifecycle (concepts apply to both authoring paths).
+- [MASM Smart Contracts](../masm/index.md) — the path mainnet supports today.
+- [Miden Standards](../standards/index.md) — reusable building blocks callable from Rust or MASM.
+- [Smart Contracts → Overview](../overview.md) — execution model and lifecycle (concepts apply to both authoring paths).
- [API reference on docs.rs](https://docs.rs/miden/latest/miden/) — full Rust SDK API documentation.
diff --git a/docs/builder/smart-contracts/standards/account-components.md b/docs/builder/smart-contracts/standards/account-components.md
index 463786c3..a28154b0 100644
--- a/docs/builder/smart-contracts/standards/account-components.md
+++ b/docs/builder/smart-contracts/standards/account-components.md
@@ -59,7 +59,7 @@ fn build_wallet_account() -> Result<(), Box> {
}
```
-For authentication details, see [Authentication](../accounts/authentication). For how component methods are authored in Rust, see [Components](../accounts/components).
+For authentication details, see [Authentication](../accounts/authentication.md). For how component methods are authored in Rust, see [Components](../accounts/components.md).
## Check note compatibility
@@ -90,6 +90,6 @@ Reach for MASM directly when you are implementing low-level behavior, integratin
## Related pages
-- [Standard notes](./standard-notes) - which account interfaces each standard note expects
-- [Faucets and policies](./faucets-and-policies) - using faucet and mint policy components
+- [Standard notes](./standard-notes.md) - which account interfaces each standard note expects
+- [Faucets and policies](./faucets-and-policies.md) - using faucet and mint policy components
- [`miden-standards` account source](https://github.com/0xMiden/protocol/tree/next/crates/miden-standards/src/account) - current implementation
diff --git a/docs/builder/smart-contracts/standards/faucets-and-policies.md b/docs/builder/smart-contracts/standards/faucets-and-policies.md
index 21841e93..ac430679 100644
--- a/docs/builder/smart-contracts/standards/faucets-and-policies.md
+++ b/docs/builder/smart-contracts/standards/faucets-and-policies.md
@@ -117,7 +117,7 @@ For standard flows:
- The recipient discovers and consumes the note.
- The recipient's account must be able to receive the asset, usually by including `BasicWallet`.
-This is the same two-transaction note model described in [What are Notes?](../notes/introduction).
+This is the same two-transaction note model described in [What are Notes?](../notes/introduction.md).
## Burn returned assets
@@ -139,7 +139,7 @@ If you only need additional public methods, compose the faucet account with an e
## Related pages
-- [Account components](./account-components) - composing faucets with standard auth and ownership components
-- [Standard notes](./standard-notes) - mint and burn notes
-- [Assets, Vault, and Faucet migration notes](../../migration/asset-vault-faucet) - asset and faucet changes
+- [Account components](./account-components.md) - composing faucets with standard auth and ownership components
+- [Standard notes](./standard-notes.md) - mint and burn notes
+- [Assets, Vault, and Faucet migration notes](../../migration/05-asset-vault-faucet.md) - asset and faucet changes
- [`miden-standards` faucet source](https://github.com/0xMiden/protocol/tree/next/crates/miden-standards/src/account/faucets) - current implementation
diff --git a/docs/builder/smart-contracts/standards/index.md b/docs/builder/smart-contracts/standards/index.md
index d5264be3..f1db8bf7 100644
--- a/docs/builder/smart-contracts/standards/index.md
+++ b/docs/builder/smart-contracts/standards/index.md
@@ -28,13 +28,13 @@ Smart Contracts is the domain. Rust and Miden Assembly are authoring paths insid
## What this section covers
-
+
Use standard wallet, authentication, access-control, faucet, and metadata components.
-
+
Choose P2ID, P2IDE, SWAP, PSWAP, mint, and burn note scripts.
-
+
Build token faucets and choose mint, burn, send, and receive policy modules.
@@ -59,5 +59,5 @@ You can mix both approaches. A typical application account starts with standard
- [Accounts](../accounts/) - components, storage, authentication, and account operations
- [Notes](../notes/) - note model, note scripts, standard note types, and output notes
-- [Cross-component calls](../cross-component-calls) - calling component interfaces from scripts and components
+- [Cross-component calls](../cross-component-calls.md) - calling component interfaces from scripts and components
- [`miden-standards` source](https://github.com/0xMiden/protocol/tree/next/crates/miden-standards) - current standards implementation
diff --git a/docs/builder/smart-contracts/standards/standard-notes.md b/docs/builder/smart-contracts/standards/standard-notes.md
index be246913..7a040ac1 100644
--- a/docs/builder/smart-contracts/standards/standard-notes.md
+++ b/docs/builder/smart-contracts/standards/standard-notes.md
@@ -20,7 +20,7 @@ Use the Rust APIs to construct standard notes in client or transaction-building
| MINT | A faucet is minting fungible tokens into a note. | `MintNote` | `miden::standards::notes::mint` |
| BURN | A faucet is burning fungible tokens returned through a note. | `BurnNote` | `miden::standards::notes::burn` |
-For the note model itself, start with [What are Notes?](../notes/introduction). This page focuses on how the standards fit into builder workflows.
+For the note model itself, start with [What are Notes?](../notes/introduction.md). This page focuses on how the standards fit into builder workflows.
```rust title="Create a public P2ID note"
use miden_protocol::Word;
@@ -96,7 +96,7 @@ The Rust types live under `miden_standards::note`. The MASM scripts live under `
## Related pages
-- [Standard Note Types](../notes/note-types) - more detail on P2ID, P2IDE, and SWAP
-- [Output Notes](../notes/output-notes) - creating output notes from transactions
-- [Note Scripts](../notes/note-scripts) - writing custom note scripts
+- [Standard Note Types](../notes/note-types.md) - more detail on P2ID, P2IDE, and SWAP
+- [Output Notes](../notes/output-notes.md) - creating output notes from transactions
+- [Note Scripts](../notes/note-scripts.md) - writing custom note scripts
- [`miden-standards` note source](https://github.com/0xMiden/protocol/tree/next/crates/miden-standards/src/note) - current implementation
diff --git a/docs/builder/smart-contracts/transactions/advice-provider.md b/docs/builder/smart-contracts/transactions/advice-provider.md
index 261208ed..bd3d2355 100644
--- a/docs/builder/smart-contracts/transactions/advice-provider.md
+++ b/docs/builder/smart-contracts/transactions/advice-provider.md
@@ -88,7 +88,7 @@ let note_type = data[1];
// ...
```
-See [Transaction Scripts](./transaction-scripts) for the full `basic-wallet-tx-script` example.
+See [Transaction Scripts](./transaction-scripts.md) for the full `basic-wallet-tx-script` example.
## Writing to the advice map
@@ -119,7 +119,7 @@ adv_insert_mem(key, start_addr, end_addr);
## Requesting a Falcon signature
-`emit_falcon_sig_to_stack` emits an `AUTH_REQUEST_EVENT` that instructs the host to push a Falcon512 signature onto the advice stack. This is typically used in [authentication components](../accounts/authentication) before calling `rpo_falcon512_verify`.
+`emit_falcon_sig_to_stack` emits an `AUTH_REQUEST_EVENT` that instructs the host to push a Falcon512 signature onto the advice stack. This is typically used in [authentication components](../accounts/authentication.md) before calling `rpo_falcon512_verify`.
```rust
use miden::intrinsics::advice::emit_falcon_sig_to_stack;
@@ -135,6 +135,6 @@ Full API docs on docs.rs: [`miden::intrinsics::advice`](https://docs.rs/miden/la
## Related
-- [Authentication](../accounts/authentication) — Falcon512 signature verification (over Poseidon2) and nonce management
-- [Transaction Scripts](./transaction-scripts) — executing logic in the transaction context
-- [Transaction Context](./transaction-context) — overview of transaction execution
+- [Authentication](../accounts/authentication.md) — Falcon512 signature verification (over Poseidon2) and nonce management
+- [Transaction Scripts](./transaction-scripts.md) — executing logic in the transaction context
+- [Transaction Context](./transaction-context.md) — overview of transaction execution
diff --git a/docs/builder/smart-contracts/transactions/introduction.md b/docs/builder/smart-contracts/transactions/introduction.md
index 61796f40..91976e0e 100644
--- a/docs/builder/smart-contracts/transactions/introduction.md
+++ b/docs/builder/smart-contracts/transactions/introduction.md
@@ -70,7 +70,7 @@ The ZK circuit **cannot produce a valid proof**. This means:
This is fundamentally different from Ethereum's `revert`, where the failed transaction still lands onchain, consumes gas, and is visible to everyone.
-A separate failure mode is the **empty transaction**: a transaction that runs to completion but mutates no account state (storage, vault, or nonce) and consumes no input notes. Both the Rust client (which raises `TransactionRequestError::NoInputNotesNorAccountChange` before submission) and the VM kernel reject it. This typically catches transaction scripts whose conditional logic takes a no-op branch — see [Empty Transaction](../../tutorials/helpers/pitfalls#empty-transaction-no-state-change-no-notes) in the pitfalls guide for the recommended pattern.
+A separate failure mode is the **empty transaction**: a transaction that runs to completion but mutates no account state (storage, vault, or nonce) and consumes no input notes. Both the Rust client (which raises `TransactionRequestError::NoInputNotesNorAccountChange` before submission) and the VM kernel reject it. This typically catches transaction scripts whose conditional logic takes a no-op branch — see [Empty Transaction](../../tutorials/helpers/pitfalls.md#empty-transaction-no-state-change-no-notes) in the pitfalls guide for the recommended pattern.
## How transactions differ from EVM transactions
diff --git a/docs/builder/smart-contracts/transactions/transaction-context.md b/docs/builder/smart-contracts/transactions/transaction-context.md
index 9414f6b2..8eea96a3 100644
--- a/docs/builder/smart-contracts/transactions/transaction-context.md
+++ b/docs/builder/smart-contracts/transactions/transaction-context.md
@@ -57,9 +57,9 @@ The expiration delta determines how many blocks after creation the transaction r
## Transaction scripts
-Transaction scripts use the `#[tx_script]` macro to define a top-level entry point for the transaction. See [Transaction Scripts](./transaction-scripts) for the full `#[tx_script]` API and examples.
+Transaction scripts use the `#[tx_script]` macro to define a top-level entry point for the transaction. See [Transaction Scripts](./transaction-scripts.md) for the full `#[tx_script]` API and examples.
-For signature verification using the transaction context, see [Authentication](../accounts/authentication). For time-based patterns using `tx::get_block_number()`, see [Patterns — Rate limiting](../patterns#rate-limiting).
+For signature verification using the transaction context, see [Authentication](../accounts/authentication.md). For time-based patterns using `tx::get_block_number()`, see [Patterns — Rate limiting](../patterns.md#rate-limiting).
:::info API Reference
Full API docs on docs.rs: [`miden::tx`](https://docs.rs/miden/latest/miden/tx/)
diff --git a/docs/builder/smart-contracts/transactions/transaction-scripts.md b/docs/builder/smart-contracts/transactions/transaction-scripts.md
index 0585e0d7..d8b54075 100644
--- a/docs/builder/smart-contracts/transactions/transaction-scripts.md
+++ b/docs/builder/smart-contracts/transactions/transaction-scripts.md
@@ -6,7 +6,7 @@ description: "Write transaction scripts with #[tx_script] to orchestrate multi-n
# Transaction Scripts
-A transaction script is a top-level function that runs once per transaction, after all note scripts have executed. Use it to orchestrate logic that spans multiple consumed notes — moving assets from the account vault into output notes, calling account methods via [cross-component calls](../cross-component-calls), or running anything that must happen after all note scripts finish.
+A transaction script is a top-level function that runs once per transaction, after all note scripts have executed. Use it to orchestrate logic that spans multiple consumed notes — moving assets from the account vault into output notes, calling account methods via [cross-component calls](../cross-component-calls.md), or running anything that must happen after all note scripts finish.
## `#[tx_script]` signature
@@ -108,11 +108,11 @@ This script uses the advice map to pass structured input data. The caller encode
:::
:::tip
-`adv_push_mapvaln` and `adv_load_preimage` are part of the advice provider — the mechanism for supplying auxiliary data to a transaction. See [Advice Provider](./advice-provider) for the full function reference.
+`adv_push_mapvaln` and `adv_load_preimage` are part of the advice provider — the mechanism for supplying auxiliary data to a transaction. See [Advice Provider](./advice-provider.md) for the full function reference.
:::
## Related
-- [Transaction Context](./transaction-context) — `tx` module (block info, note commitments, expiration)
-- [Cross-Component Calls](../cross-component-calls) — how `&mut Account` works in tx scripts
-- [Reading Notes](../notes/reading-notes) — reading input notes by index inside tx scripts
+- [Transaction Context](./transaction-context.md) — `tx` module (block info, note commitments, expiration)
+- [Cross-Component Calls](../cross-component-calls.md) — how `&mut Account` works in tx scripts
+- [Reading Notes](../notes/reading-notes.md) — reading input notes by index inside tx scripts
diff --git a/docs/builder/smart-contracts/types.md b/docs/builder/smart-contracts/types.md
index 5a4da414..0a06f3b2 100644
--- a/docs/builder/smart-contracts/types.md
+++ b/docs/builder/smart-contracts/types.md
@@ -6,7 +6,7 @@ description: "Felt field arithmetic, Word layout, Asset encoding, and type conve
# Types
-Miden's type system is built around field elements rather than standard integers. All computation inside the Miden VM is modular arithmetic over the Goldilocks prime field ($p = 2^{64} - 2^{32} + 1$), so overflow and division behave differently from standard integers. `Felt` is the native numeric type, `Word` is a tuple of four Felts used for [storage](./accounts/storage) and hashing, and `Asset` encodes fungible and non-fungible assets as Words.
+Miden's type system is built around field elements rather than standard integers. All computation inside the Miden VM is modular arithmetic over the Goldilocks prime field ($p = 2^{64} - 2^{32} + 1$), so overflow and division behave differently from standard integers. `Felt` is the native numeric type, `Word` is a tuple of four Felts used for [storage](./accounts/storage.md) and hashing, and `Asset` encodes fungible and non-fungible assets as Words.
## Felt — Field elements
@@ -285,7 +285,7 @@ The SDK also provides `NoteIdx`, `Tag`, `NoteType`, `Recipient`, `Digest`, and `
| `[u32; 4]` / `[u16; 4]` / `[u8; 4]` / `[bool; 4]` | `Word` | `Word::from(arr)` |
| `Word` | `[Felt; 4]` | `w.into_elements()` or `let arr: [Felt; 4] = w.into()` |
-Use these types in [component definitions](./accounts/components), store and retrieve Words from [persistent storage](./accounts/storage), or define your own types for public APIs with [`#[export_type]`](./accounts/custom-types).
+Use these types in [component definitions](./accounts/components.md), store and retrieve Words from [persistent storage](./accounts/storage.md), or define your own types for public APIs with [`#[export_type]`](./accounts/custom-types.md).
:::info API Reference
Full API docs on docs.rs: [`Felt`](https://docs.rs/miden/latest/miden/struct.Felt.html), [`Word`](https://docs.rs/miden/latest/miden/struct.Word.html), [`Asset`](https://docs.rs/miden/latest/miden/struct.Asset.html)
diff --git a/docs/builder/tools/bridging/index.md b/docs/builder/tools/bridging/index.md
index 2414eb1a..a54dfb54 100644
--- a/docs/builder/tools/bridging/index.md
+++ b/docs/builder/tools/bridging/index.md
@@ -24,15 +24,15 @@ mainnet funds.
## Start here
-
+
Run the mock 1Click Bridge API locally and reproduce the Sepolia-to-Miden
and Miden-to-Sepolia testing flow.
-
+
Understand the actors, solver role, public Miden notes, and inbound/outbound
lifecycle.
-
+
Integration shape for `/v0/tokens`, `/v0/quote`, `/v0/deposit/submit`, and
`/v0/status`.
@@ -58,4 +58,4 @@ Use these docs when you need to:
For core account, note, and transaction concepts, start with the Smart Contracts
section instead. For testnet RPC, explorer, faucet, and remote prover endpoints,
-see [Network](../network).
+see [Network](../network.md).
diff --git a/docs/builder/tools/bridging/testnet-sandbox.md b/docs/builder/tools/bridging/testnet-sandbox.md
index f3efe302..f517823f 100644
--- a/docs/builder/tools/bridging/testnet-sandbox.md
+++ b/docs/builder/tools/bridging/testnet-sandbox.md
@@ -95,7 +95,7 @@ make sepolia-reset
The `/demo/*` and `/lab` endpoints in the sandbox are local helpers. App
integrations should use only the `/v0/*` endpoints described in the
-[API reference](./api-reference).
+[API reference](./api-reference.md).
## Run live testnet evidence
diff --git a/docs/builder/tools/clients/index.md b/docs/builder/tools/clients/index.md
index 31350c95..545c6409 100644
--- a/docs/builder/tools/clients/index.md
+++ b/docs/builder/tools/clients/index.md
@@ -15,10 +15,10 @@ The Miden client manages accounts, builds and executes transactions, produces ze
Native Rust library and CLI. Best for services, proving infrastructure, tests, scripting, and local exploration.
-
+
`@miden-sdk/miden-sdk` — Rust compiled to WebAssembly with a typed TypeScript API. Browser, Node, Electron, service workers.
-
+
`@miden-sdk/react` — `MidenProvider` + hooks (`useMiden`, `useAccount`, `useSend`, …) wrapping the Web SDK.
@@ -43,13 +43,13 @@ The Miden client manages accounts, builds and executes transactions, produces ze
## Shared topics
-
+
Run a local node, point Rust and web clients at localhost, import genesis accounts, and debug local transaction state.
Errors, diagnostic output, and recovery patterns shared across all surfaces.
-
+
End-to-end walkthroughs using each client surface — Miden Bank, recipes, helpers.
diff --git a/docs/builder/tools/index.md b/docs/builder/tools/index.md
index 913c3535..0610378b 100644
--- a/docs/builder/tools/index.md
+++ b/docs/builder/tools/index.md
@@ -14,10 +14,10 @@ Developer tools for building on and interacting with the Miden network. Use the
Full-featured Rust library for Miden layer 2 integration — accounts, transactions, notes, proving.
-
+
Browser-based client for managing accounts and transactions from a web app.
-
+
Hooks and components for Miden dApps.
@@ -25,19 +25,19 @@ Developer tools for building on and interacting with the Miden network. Use the
## Toolchain + environments
-
+
Install and switch between Miden toolchain channels — VM, compiler, client, stdlib, kernel — from a unified `miden` command.
-
+
Interactive environment for writing and testing Miden Assembly programs.
-
+
Live Miden testnet endpoints — status, block explorer (MidenScan), RPC, faucet, remote prover.
Offchain relay service for delivering private note payloads between senders and recipients.
-
+
Testnet bridge tooling and interoperability guides, starting with the mock 1Click bridge sandbox.
diff --git a/docs/builder/tools/midenup.md b/docs/builder/tools/midenup.md
index fa5f0239..f2cbc5b3 100644
--- a/docs/builder/tools/midenup.md
+++ b/docs/builder/tools/midenup.md
@@ -105,13 +105,13 @@ Everything outside the alias table is forwarded to the underlying binary — e.g
## Related
-
+
Full environment setup — prerequisites, node install, first account.
-
+
Walk through `miden account`, `miden send`, `miden faucet`, and the rest.
-
+
Endpoints the `miden` CLI points at — RPC, faucet, remote prover, block explorer.
diff --git a/docs/builder/tools/network.md b/docs/builder/tools/network.md
index 39de62f8..85445d8c 100644
--- a/docs/builder/tools/network.md
+++ b/docs/builder/tools/network.md
@@ -46,7 +46,7 @@ Hard-coding testnet URLs in client configs is fine for demos, but the Miden ops
-**Install the toolchain** — `midenup` pulls the client, CLI, and compiler in one step. See [Installation](../get-started/setup/installation).
+**Install the toolchain** — `midenup` pulls the client, CLI, and compiler in one step. See [Installation](../get-started/setup/installation.md).
**Point the client at testnet** — default config targets the testnet RPC, so `miden new` projects work out of the box.
@@ -61,7 +61,7 @@ Hard-coding testnet URLs in client configs is fine for demos, but the Miden ops
## Related
-
+
End-to-end walkthrough — client → RPC → MidenScan verification.
diff --git a/docs/builder/tools/playground.md b/docs/builder/tools/playground.md
index 3a23dd40..709c2968 100644
--- a/docs/builder/tools/playground.md
+++ b/docs/builder/tools/playground.md
@@ -32,19 +32,19 @@ An interactive browser environment for writing, compiling, and executing Miden A
-The Playground shines for learning MASM and for quick prototyping. For anything bigger than a snippet — components, storage, note dispatch, transaction flows — move to a `miden new` Rust project locally. See [your first smart contract](../get-started/your-first-smart-contract) for the handoff.
+The Playground shines for learning MASM and for quick prototyping. For anything bigger than a snippet — components, storage, note dispatch, transaction flows — move to a `miden new` Rust project locally. See [your first smart contract](../get-started/your-first-smart-contract/index.md) for the handoff.
## Related
-
+
Install the toolchain and build + deploy a counter contract in Rust.
-
+
New `word(...)` / `event(...)` constants, `std::math::u128`, and other MASM-level deltas.
-
+
Accounts, notes, transactions, and the Rust SDK surface.
diff --git a/docs/builder/tutorials/helpers/pitfalls.md b/docs/builder/tutorials/helpers/pitfalls.md
index 965f3d08..1a5c2040 100644
--- a/docs/builder/tutorials/helpers/pitfalls.md
+++ b/docs/builder/tutorials/helpers/pitfalls.md
@@ -542,7 +542,7 @@ let request = TransactionRequestBuilder::new()
```
:::tip Standard auth handles this for you
-Most account templates run an authentication procedure that calls `incr_nonce()` on every transaction. If your account uses `BasicWallet`, `IncrNonceAuthComponent`, or any auth component that increments the nonce, you only hit this pitfall in transaction-script-only flows that skip the auth path. See [Authentication](../../smart-contracts/accounts/authentication) for details.
+Most account templates run an authentication procedure that calls `incr_nonce()` on every transaction. If your account uses `BasicWallet`, `IncrNonceAuthComponent`, or any auth component that increments the nonce, you only hit this pitfall in transaction-script-only flows that skip the auth path. See [Authentication](../../smart-contracts/accounts/authentication.md) for details.
:::
### Why this exists
@@ -551,7 +551,7 @@ A Miden transaction commits to a state delta plus a set of consumed notes. A tra
:::info See also
- Client-side error catalog: [`TransactionRequestError::NoInputNotesNorAccountChange`](../../tools/clients/common-errors)
-- Failure modes table: [Account Operations](../../smart-contracts/accounts/account-operations#when-proof-generation-fails)
+- Failure modes table: [Account Operations](../../smart-contracts/accounts/account-operations.md#when-proof-generation-fails)
:::
---
@@ -576,6 +576,6 @@ See these patterns in context in the [miden-bank repository](https://github.com/
## Next Steps
-- **[Debugging Guide](./debugging)** - Troubleshoot errors
-- **[Testing Guide](./testing)** - MockChain patterns
+- **[Debugging Guide](./debugging.md)** - Troubleshoot errors
+- **[Testing Guide](./testing.md)** - MockChain patterns
- **[Miden Bank Tutorial](../miden-bank/)** - See these patterns in context
diff --git a/docs/builder/tutorials/helpers/testing.md b/docs/builder/tutorials/helpers/testing.md
index 2de3fd19..2fbcf2db 100644
--- a/docs/builder/tutorials/helpers/testing.md
+++ b/docs/builder/tutorials/helpers/testing.md
@@ -621,6 +621,6 @@ See the complete test implementations in the [miden-bank repository](https://git
## Next Steps
-- **[Debugging Guide](./debugging)** - Troubleshoot common issues
-- **[Common Pitfalls](./pitfalls)** - Avoid known gotchas
+- **[Debugging Guide](./debugging.md)** - Troubleshoot common issues
+- **[Common Pitfalls](./pitfalls.md)** - Avoid known gotchas
- **[Miden Bank Tutorial](../miden-bank/)** - See testing in action
diff --git a/docs/builder/tutorials/index.md b/docs/builder/tutorials/index.md
index 9d19467d..127e6d0b 100644
--- a/docs/builder/tutorials/index.md
+++ b/docs/builder/tutorials/index.md
@@ -28,19 +28,19 @@ Hands-on walkthroughs for building on Miden. Every tutorial pairs with runnable
## Development helpers
-
+
Test your contracts against MockChain for local simulation.
-
+
Interpret errors and debug common issues.
-
+
Avoid known issues and limitations.
## Prerequisites
-- [Install the Miden toolchain](../get-started/setup/installation) with `midenup`.
+- [Install the Miden toolchain](../get-started/setup/installation.md) with `midenup`.
- Basic familiarity with Rust (or TypeScript for the client examples).
-- Understanding of the [core concepts](../smart-contracts/) — accounts, notes, transactions.
+- Understanding of the [core concepts](../smart-contracts/index.md) — accounts, notes, transactions.
diff --git a/package.json b/package.json
index 27297f71..03c5d20f 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,9 @@
"generate:og": "node scripts/generate-og-images.mjs",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
+ "check:doc-links": "node scripts/normalize-doc-links.mjs",
+ "normalize-doc-links": "node scripts/normalize-doc-links.mjs --fix",
+ "normalize-doc-links:all": "node scripts/normalize-doc-links.mjs --fix --all",
"typecheck": "tsc"
},
"dependencies": {
diff --git a/scripts/normalize-doc-links.mjs b/scripts/normalize-doc-links.mjs
new file mode 100644
index 00000000..63c68dd0
--- /dev/null
+++ b/scripts/normalize-doc-links.mjs
@@ -0,0 +1,304 @@
+#!/usr/bin/env node
+
+import fs from "node:fs";
+import path from "node:path";
+
+const repoRoot = process.cwd();
+const fix = process.argv.includes("--fix");
+// By default only the current docs (docs/) are scanned, so `check` stays green
+// against the committed tree. Pass --all to additionally scan every
+// versioned_docs/ snapshot — used at deploy time to heal historical
+// versions, which are intentionally not normalized in-repo.
+const includeVersioned = process.argv.includes("--all");
+const markdownExtensions = new Set([".md", ".mdx"]);
+const versionedRoot = path.join(repoRoot, "versioned_docs");
+const docsRoots = [
+ path.join(repoRoot, "docs"),
+ ...(includeVersioned && fs.existsSync(versionedRoot)
+ ? fs
+ .readdirSync(versionedRoot, { withFileTypes: true })
+ .filter((entry) => entry.isDirectory())
+ .map((entry) => path.join(versionedRoot, entry.name))
+ : []),
+];
+
+function walk(dir) {
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
+ const files = [];
+
+ for (const entry of entries) {
+ const fullPath = path.join(dir, entry.name);
+
+ if (entry.isDirectory()) {
+ files.push(...walk(fullPath));
+ } else if (markdownExtensions.has(path.extname(entry.name))) {
+ files.push(fullPath);
+ }
+ }
+
+ return files;
+}
+
+function toPosix(filePath) {
+ return filePath.split(path.sep).join(path.posix.sep);
+}
+
+function getRoot(filePath) {
+ return docsRoots.find((root) => filePath.startsWith(`${root}${path.sep}`));
+}
+
+function splitUrl(rawUrl) {
+ const suffixIndex = rawUrl.search(/[?#]/);
+
+ if (suffixIndex === -1) {
+ return { pathname: rawUrl, suffix: "" };
+ }
+
+ return {
+ pathname: rawUrl.slice(0, suffixIndex),
+ suffix: rawUrl.slice(suffixIndex),
+ };
+}
+
+function isRelativeDocCandidate(rawUrl) {
+ if (!(rawUrl.startsWith("./") || rawUrl.startsWith("../"))) {
+ return false;
+ }
+
+ const { pathname } = splitUrl(rawUrl);
+ return !path.posix.extname(pathname);
+}
+
+function candidateFiles(root, sourceFile, rawUrl) {
+ const { pathname } = splitUrl(rawUrl);
+ const sourceDir = path.posix.dirname(
+ toPosix(path.relative(root, sourceFile)),
+ );
+ const target = path.posix.normalize(path.posix.join(sourceDir, pathname));
+ const rootPosix = toPosix(root);
+
+ const directCandidates = [
+ `${target}.md`,
+ `${target}.mdx`,
+ path.posix.join(target, "index.md"),
+ path.posix.join(target, "index.mdx"),
+ ].map((candidate) => path.join(rootPosix, candidate));
+
+ return [
+ ...directCandidates,
+ ...numberPrefixedCandidates(rootPosix, target),
+ ];
+}
+
+function numberPrefixedCandidates(root, target) {
+ const targetDir = path.posix.dirname(target);
+ const targetBase = path.posix.basename(target);
+ const absoluteDir = path.join(root, targetDir);
+
+ if (!fs.existsSync(absoluteDir)) {
+ return [];
+ }
+
+ const candidates = [];
+ const prefixPattern = new RegExp(`^\\d+[-_]${escapeRegExp(targetBase)}(?:\\.mdx?)?$`);
+
+ for (const entry of fs.readdirSync(absoluteDir, { withFileTypes: true })) {
+ if (!prefixPattern.test(entry.name)) {
+ continue;
+ }
+
+ const candidate = path.join(absoluteDir, entry.name);
+
+ if (entry.isFile() && markdownExtensions.has(path.extname(entry.name))) {
+ candidates.push(candidate);
+ }
+
+ if (entry.isDirectory()) {
+ candidates.push(path.join(candidate, "index.md"));
+ candidates.push(path.join(candidate, "index.mdx"));
+ }
+ }
+
+ return candidates;
+}
+
+function escapeRegExp(value) {
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
+}
+
+function stripNumberPrefix(value) {
+ return value.replace(/^\d+[-_]/, "");
+}
+
+function resolveTarget(root, sourceFile, rawUrl) {
+ for (const candidate of candidateFiles(root, sourceFile, rawUrl)) {
+ if (fs.existsSync(candidate)) {
+ return candidate;
+ }
+ }
+
+ return undefined;
+}
+
+function relativeMarkdownPath(sourceFile, targetFile, suffix) {
+ const sourceDir = path.posix.dirname(toPosix(sourceFile));
+ let relativePath = path.posix.relative(sourceDir, toPosix(targetFile));
+
+ if (!relativePath.startsWith(".")) {
+ relativePath = `./${relativePath}`;
+ }
+
+ return `${relativePath}${suffix}`;
+}
+
+function docId(root, targetFile) {
+ return toPosix(path.relative(root, targetFile))
+ .replace(/\.mdx?$/, "")
+ .split("/")
+ .map(stripNumberPrefix)
+ .join("/");
+}
+
+function normalizeMarkdownLink(root, sourceFile, rawUrl) {
+ if (!isRelativeDocCandidate(rawUrl)) {
+ return rawUrl;
+ }
+
+ const target = resolveTarget(root, sourceFile, rawUrl);
+
+ if (!target) {
+ return rawUrl;
+ }
+
+ const { suffix } = splitUrl(rawUrl);
+ return relativeMarkdownPath(sourceFile, target, suffix);
+}
+
+// Link-text alternation: backtick-quoted code (which may contain `]`) or any
+// non-`]`, non-backtick character. Lets the regex traverse text like
+// `[\`#[export_type]\`]`. The two branches are mutually exclusive on the first
+// character — keeps the engine off exponential backtracking paths.
+const linkTextPattern = String.raw`(?:\`[^\`\n]*\`|[^\]\`])*?`;
+
+// Fenced code block (``` or ~~~) or HTML/MDX comment. Anything matched here is
+// passed through untouched so example markdown in docs isn't rewritten.
+// Indented fences (inside list items) are not supported — they trigger
+// catastrophic regex backtracking on large docs.
+const protectedRegionPattern =
+ /(?:^(`{3,}|~{3,})[^\n]*\n[\s\S]*?^\1[^\n]*$|)/gm;
+
+function replaceOutsideProtectedRegions(content, transform) {
+ let result = "";
+ let lastIndex = 0;
+
+ for (const match of content.matchAll(protectedRegionPattern)) {
+ result += transform(content.slice(lastIndex, match.index));
+ result += match[0];
+ lastIndex = match.index + match[0].length;
+ }
+
+ result += transform(content.slice(lastIndex));
+ return result;
+}
+
+function normalizeMarkdownLinks(root, sourceFile, content) {
+ let normalized = replaceOutsideProtectedRegions(content, (chunk) =>
+ chunk.replace(
+ new RegExp(
+ `(!?\\[${linkTextPattern}\\]\\()(<[^>\\s]+>|[^\\s)]+)([^)]*\\))`,
+ "g",
+ ),
+ (match, prefix, rawUrl, suffix) => {
+ const wrapped = rawUrl.startsWith("<") && rawUrl.endsWith(">");
+ const url = wrapped ? rawUrl.slice(1, -1) : rawUrl;
+ const normalizedUrl = normalizeMarkdownLink(root, sourceFile, url);
+
+ if (normalizedUrl === url) {
+ return match;
+ }
+
+ return `${prefix}${wrapped ? `<${normalizedUrl}>` : normalizedUrl}${suffix}`;
+ },
+ ),
+ );
+
+ normalized = replaceOutsideProtectedRegions(normalized, (chunk) =>
+ chunk.replace(
+ /^(\s*\[[^\]]+\]:\s+)(\S+)(.*)$/gm,
+ (match, prefix, rawUrl, suffix) => {
+ const normalizedUrl = normalizeMarkdownLink(root, sourceFile, rawUrl);
+
+ if (normalizedUrl === rawUrl) {
+ return match;
+ }
+
+ return `${prefix}${normalizedUrl}${suffix}`;
+ },
+ ),
+ );
+
+ return normalized;
+}
+
+function normalizeCardLinks(root, sourceFile, content) {
+ return replaceOutsideProtectedRegions(content, (chunk) =>
+ chunk.replace(/\n]*\bhref="([^"]+)"[^>\n]*>/g, (tag, rawUrl) => {
+ if (!isRelativeDocCandidate(rawUrl)) {
+ return tag;
+ }
+
+ const target = resolveTarget(root, sourceFile, rawUrl);
+
+ if (!target) {
+ return tag;
+ }
+
+ const { suffix } = splitUrl(rawUrl);
+
+ // Bail out on query strings — Card has no prop for them and silently
+ // dropping a query suffix would change the link target.
+ if (suffix && !suffix.startsWith("#")) {
+ return tag;
+ }
+
+ const id = docId(root, target);
+ return tag.replace(
+ `href="${rawUrl}"`,
+ `docId="${id}"${suffix ? ` hash="${suffix}"` : ""}`,
+ );
+ }),
+ );
+}
+
+const changedFiles = [];
+
+for (const root of docsRoots) {
+ for (const file of walk(root)) {
+ const content = fs.readFileSync(file, "utf8");
+ const normalized = normalizeCardLinks(
+ root,
+ file,
+ normalizeMarkdownLinks(root, file, content),
+ );
+
+ if (normalized !== content) {
+ changedFiles.push(path.relative(repoRoot, file));
+
+ if (fix) {
+ fs.writeFileSync(file, normalized);
+ }
+ }
+ }
+}
+
+if (changedFiles.length > 0) {
+ console.log(
+ `${fix ? "Updated" : "Would update"} ${changedFiles.length} docs files.`,
+ );
+ for (const file of changedFiles) {
+ console.log(file);
+ }
+ process.exit(fix ? 0 : 1);
+}
+
+console.log("Docs links are already normalized.");
diff --git a/src/components/mdx/Card/index.tsx b/src/components/mdx/Card/index.tsx
index 74e66e63..efd08fc4 100644
--- a/src/components/mdx/Card/index.tsx
+++ b/src/components/mdx/Card/index.tsx
@@ -1,16 +1,160 @@
import React, { type ReactNode } from "react";
import Link from "@docusaurus/Link";
+import {
+ useActiveDocContext,
+ useLayoutDoc,
+ type GlobalDoc,
+ type GlobalVersion,
+} from "@docusaurus/plugin-content-docs/client";
import clsx from "clsx";
import styles from "./styles.module.css";
type CardProps = {
title: string;
href?: string;
+ docId?: string;
+ hash?: string;
icon?: ReactNode;
eyebrow?: string;
children?: ReactNode;
};
+type CardLinkProps = {
+ href?: string;
+ docId?: string;
+ hash?: string;
+ children: ReactNode;
+};
+
+function splitHref(href: string): { pathname: string; suffix: string } {
+ const suffixIndex = href.search(/[?#]/);
+
+ if (suffixIndex === -1) {
+ return { pathname: href, suffix: "" };
+ }
+
+ return {
+ pathname: href.slice(0, suffixIndex),
+ suffix: href.slice(suffixIndex),
+ };
+}
+
+function isRelativePath(href: string): boolean {
+ return href.startsWith("./") || href.startsWith("../");
+}
+
+function normalizeDocId(pathname: string): string {
+ const parts: string[] = [];
+
+ for (const part of pathname.split("/")) {
+ if (!part || part === ".") {
+ continue;
+ }
+
+ if (part === "..") {
+ parts.pop();
+ continue;
+ }
+
+ parts.push(part);
+ }
+
+ return parts.join("/");
+}
+
+function getDocSourceDir(docId: string): string {
+ const indexSuffix = "/index";
+
+ if (docId.endsWith(indexSuffix)) {
+ return docId.slice(0, -indexSuffix.length);
+ }
+
+ return docId.split("/").slice(0, -1).join("/");
+}
+
+function findVersionDoc(
+ version: GlobalVersion,
+ targetId: string,
+): GlobalDoc | undefined {
+ return version.docs.find(
+ (doc) => doc.id === targetId || doc.id === `${targetId}/index`,
+ );
+}
+
+function getVersionPath(version: GlobalVersion): string {
+ return version.path === "/" ? "" : version.path.replace(/\/$/, "");
+}
+
+function resolveDocRelativeHref(
+ href: string,
+ activeDoc: GlobalDoc | undefined,
+ activeVersion: GlobalVersion | undefined,
+): string {
+ if (!activeDoc || !activeVersion) {
+ return href;
+ }
+
+ const { pathname, suffix } = splitHref(href);
+ const sourceDir = getDocSourceDir(activeDoc.id);
+ const targetId = normalizeDocId(`${sourceDir}/${pathname}`);
+ const targetDoc = findVersionDoc(activeVersion, targetId);
+
+ if (targetDoc) {
+ return `${targetDoc.path}${suffix}`;
+ }
+
+ return `${getVersionPath(activeVersion)}/${targetId.replace(/\/$/, "")}/${suffix}`;
+}
+
+function DocIdCardLink({ docId, hash, children }: CardLinkProps): JSX.Element {
+ const doc = useLayoutDoc(docId!);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function RelativeCardLink({ href, children }: CardLinkProps): JSX.Element {
+ const { activeDoc, activeVersion } = useActiveDocContext(undefined);
+ const to = resolveDocRelativeHref(href!, activeDoc, activeVersion);
+
+ return (
+
+ {children}
+
+ );
+}
+
+function CardLink({
+ href,
+ docId,
+ hash,
+ children,
+}: CardLinkProps): JSX.Element {
+ if (docId) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ if (href && isRelativePath(href)) {
+ return {children};
+ }
+
+ return (
+
+ {children}
+
+ );
+}
+
/**
* `` — a standalone bordered card for MDX docs. Use inside a
* `` for grouped layouts, or drop it inline for single-link cards.
@@ -18,6 +162,8 @@ type CardProps = {
export default function Card({
title,
href,
+ docId,
+ hash,
icon,
eyebrow,
children,
@@ -32,7 +178,7 @@ export default function Card({
{eyebrow ? {eyebrow} : null}