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 21a58a83..46e40a25 100644
--- a/.github/workflows/deploy-docs.yml
+++ b/.github/workflows/deploy-docs.yml
@@ -317,6 +317,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 b25fad61..74ef9e26 100644
--- a/docs/builder/get-started/accounts.md
+++ b/docs/builder/get-started/accounts.md
@@ -113,7 +113,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
@@ -134,7 +134,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
@@ -165,7 +165,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
@@ -406,6 +406,6 @@ Faucet account ID: 0xde0ba31282f7522046d3d4af40722b
- **BasicFungibleFaucet**: Enables token minting capabilities
- **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 b77bc8a0..0c3110c2 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 a0ee4e1e..4ae69074 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 d2cbf59d..009c173d 100644
--- a/docs/builder/get-started/your-first-smart-contract/create.md
+++ b/docs/builder/get-started/your-first-smart-contract/create.md
@@ -260,6 +260,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 76b4917f..d9fcd88a 100644
--- a/docs/builder/get-started/your-first-smart-contract/test.md
+++ b/docs/builder/get-started/your-first-smart-contract/test.md
@@ -304,7 +304,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 cc08cb81..239e5200 100644
--- a/docs/builder/index.md
+++ b/docs/builder/index.md
@@ -11,10 +11,10 @@ Accounts, notes, and transactions — authored in Rust, compiled to MASM, proved
## 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.
@@ -22,16 +22,16 @@ Accounts, notes, and transactions — authored in Rust, compiled to MASM, proved
## 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.
@@ -39,13 +39,13 @@ Accounts, notes, and transactions — authored in Rust, compiled to MASM, proved
## 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.
@@ -53,10 +53,10 @@ Accounts, notes, and transactions — authored in Rust, compiled to MASM, proved
## 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 2ed303ee..cb0b22b0 100644
--- a/docs/builder/miden-guardian/index.md
+++ b/docs/builder/miden-guardian/index.md
@@ -55,22 +55,22 @@ Guardian has an explicit trust boundary:
## Learn more
-
+
Roles, trust boundaries, state flow, and failure behavior.
-
+
State, deltas, commitments, and delta proposals.
-
+
API, authentication, storage, and other server components.
-
+
Trust model, integrity guarantees, and edge cases.
-
+
How to run, deploy, and troubleshoot a Guardian server.
-
+
Multi-party threshold signature workflows powered by Guardian.
diff --git a/docs/builder/miden-guardian/operator-guide/running.md b/docs/builder/miden-guardian/operator-guide/running.md
index 463c4f17..e57cdc15 100644
--- a/docs/builder/miden-guardian/operator-guide/running.md
+++ b/docs/builder/miden-guardian/operator-guide/running.md
@@ -103,4 +103,4 @@ cargo run -p guardian-server --features postgres --bin server
| `PUT /delta/proposal` | Signed headers |
| `/dashboard/accounts*` | Dashboard session |
-Signed requests carry three headers: `x-pubkey`, `x-signature`, and `x-timestamp`. Timestamps must be within ±5 minutes of server time and strictly greater than the last value the server saw for that public key — see [Authentication failures](./troubleshooting#authentication-failures) if requests are being rejected.
+Signed requests carry three headers: `x-pubkey`, `x-signature`, and `x-timestamp`. Timestamps must be within ±5 minutes of server time and strictly greater than the last value the server saw for that public key — see [Authentication failures](./troubleshooting.md#authentication-failures) if requests are being rejected.
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 dcefd68f..a085bb21 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
@@ -125,7 +125,7 @@ impl ManagedWallet {
}
```
-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 9853b4f9..33f57e49 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.14 unifies the previous per-scheme components (`AuthFalcon512Rpo`, `AuthEcdsaK256Keccak`, …) into a single scheme-agnostic [`AuthSingleSig`](https://docs.rs/miden-standards/latest/miden_standards/account/auth/struct.AuthSingleSig.html) component that takes an `AuthScheme` enum (`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
@@ -87,7 +87,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)
@@ -95,6 +95,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 7e012989..d799fdae 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
@@ -118,7 +118,7 @@ pub fn do_something(&mut self) {
### 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
@@ -165,7 +165,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 803c2e52..6126bffa 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 1fc5ead0..7600ef85 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 9d284883..c0fcdb46 100644
--- a/docs/builder/smart-contracts/accounts/storage.md
+++ b/docs/builder/smart-contracts/accounts/storage.md
@@ -6,7 +6,7 @@ description: "Persistent state management with Value slots and StorageMaps in Mi
# Storage
-Miden accounts have persistent storage organized into up to 255 name-addressable slots. Each slot holds either a single [`Word`](../types) (via `Value`) or a key-value map (via `StorageMap`). Slots are identified by `StorageSlotId` values derived from slot names, which in turn are derived from the component package name and the field name. Renaming a field changes the slot ID and is a breaking change for stored data.
+Miden accounts have persistent storage organized into up to 255 name-addressable slots. Each slot holds either a single [`Word`](../types.md) (via `Value`) or a key-value map (via `StorageMap`). Slots are identified by `StorageSlotId` values derived from slot names, which in turn are derived from the component package name and the field name. Renaming a field changes the slot ID and is a breaking change for stored data.
## Storage slots
@@ -174,7 +174,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 ff3479d5..dfe9b545 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 3ae40897..425c4f7a 100644
--- a/docs/builder/smart-contracts/index.md
+++ b/docs/builder/smart-contracts/index.md
@@ -6,27 +6,27 @@ 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.
:::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.
+If you're new to Miden, the hands-on [Miden Bank Tutorial](../tutorials/miden-bank/index.md) walks through the full lifecycle using the Rust SDK; the concepts (accounts, notes, transactions, components) translate directly to MASM.
## 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.
@@ -43,13 +43,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 f2409bd3..74dc555e 100644
--- a/docs/builder/smart-contracts/masm/index.md
+++ b/docs/builder/smart-contracts/masm/index.md
@@ -19,10 +19,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
@@ -41,11 +41,11 @@ Practical, end-to-end tutorials are in progress in the [Miden tutorials reposito
| Testing with MockChain | In progress |
| Debugging MASM contracts | In progress |
-In the meantime, the existing [Rust-based Miden Bank tutorial](../../tutorials/miden-bank/) is useful for understanding the overall transaction lifecycle and the patterns that translate to MASM. The [Reference assembly section](/reference/miden-vm/user_docs/assembly/) covers the language itself.
+In the meantime, the existing [Rust-based Miden Bank tutorial](../../tutorials/miden-bank/index.md) is useful for understanding the overall transaction lifecycle and the patterns that translate to MASM. The [Reference assembly section](/reference/miden-vm/user_docs/assembly/) covers the language itself.
## 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 ecbaedba..ed198131 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 cryptographic 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 cryptographic 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 SDK API.
+Only someone who knows these values can construct a valid consumption proof. See [Computing a Recipient](./output-notes.md#computing-a-recipient) for the SDK API.
## 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 17160e9f..8a45f91e 100644
--- a/docs/builder/smart-contracts/notes/note-scripts.md
+++ b/docs/builder/smart-contracts/notes/note-scripts.md
@@ -88,7 +88,7 @@ pub fn run(self, _arg: Word, account: &mut Account) {
}
```
-The `Account` type is auto-generated from the bindings of the dependent component — see [Cross-Component Calls](../cross-component-calls).
+The `Account` type is auto-generated from the bindings of the dependent component — see [Cross-Component Calls](../cross-component-calls.md).
### Without account access
@@ -132,7 +132,7 @@ impl CounterNote {
}
```
-This note doesn't take `&mut Account` — instead it calls the counter contract's methods directly through generated bindings. See [Cross-Component Calls](../cross-component-calls).
+This note doesn't take `&mut Account` — instead it calls the counter contract's methods directly through generated bindings. See [Cross-Component Calls](../cross-component-calls.md).
## Cargo.toml for note scripts
@@ -149,5 +149,5 @@ project-kind = "note-script"
## Related
-- [Cross-Component Calls](../cross-component-calls) — how `bindings::Account` and `counter_contract::` calls work
-- [Transaction Context](../transactions/transaction-context) — transaction scripts with `#[tx_script]`
+- [Cross-Component Calls](../cross-component-calls.md) — how `bindings::Account` and `counter_contract::` calls 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 0d9d7d25..9792c427 100644
--- a/docs/builder/smart-contracts/notes/note-types.md
+++ b/docs/builder/smart-contracts/notes/note-types.md
@@ -159,8 +159,8 @@ SwapNote::create(
Returns a tuple of `(Note, NoteDetails)` — the SWAP note to submit and the expected payback note details (for tracking).
-`NoteAttachment` is defined in the `miden-standards` crate (not the core `miden` SDK). It wraps the attachment data that gets set on output notes — see [note attachments](./output-notes#note-attachments) for the underlying SDK API.
+`NoteAttachment` is defined in the `miden-standards` crate (not the core `miden` SDK). It wraps the attachment data that gets set on output notes — 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 e3a1bc77..a032e3ef 100644
--- a/docs/builder/smart-contracts/notes/output-notes.md
+++ b/docs/builder/smart-contracts/notes/output-notes.md
@@ -54,7 +54,7 @@ Returns a `NoteMetadata` struct (not a raw `Word`):
let metadata: NoteMetadata = output_note::get_metadata(note_idx);
```
-See [Reading Notes — Note metadata](./reading-notes#note-metadata) for the `NoteMetadata` struct definition.
+See [Reading Notes — Note metadata](./reading-notes.md#note-metadata) for the `NoteMetadata` struct definition.
## Note attachments
diff --git a/docs/builder/smart-contracts/notes/reading-notes.md b/docs/builder/smart-contracts/notes/reading-notes.md
index 42c9f3ab..86219d27 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 f330fd92..0c547b33 100644
--- a/docs/builder/smart-contracts/overview.md
+++ b/docs/builder/smart-contracts/overview.md
@@ -7,10 +7,10 @@ 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/).
+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/index.md).
## What makes Miden different
@@ -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.
@@ -76,7 +76,7 @@ impl MyWallet {
Each component defines its own storage layout and public methods. The `#[component]` 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
@@ -135,7 +135,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
@@ -154,23 +154,23 @@ Miden supports four account types, set at deployment time. The same types are us
| 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) | RPO-Falcon512 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) | RPO-Falcon512 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).
+Ready to start building? The [Miden Bank Tutorial](../tutorials/miden-bank/index.md) 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..fa7d5c2b 100644
--- a/docs/builder/smart-contracts/patterns.md
+++ b/docs/builder/smart-contracts/patterns.md
@@ -6,7 +6,7 @@ description: "Common patterns and security considerations for Miden smart contra
# Patterns
-Security considerations and common patterns for Miden smart contracts. For runnable examples, see the [compiler examples directory](https://github.com/0xMiden/compiler/tree/next/examples) and the [Miden Bank Tutorial](../tutorials/miden-bank/).
+Security considerations and common patterns for Miden smart contracts. For runnable examples, see the [compiler examples directory](https://github.com/0xMiden/compiler/tree/next/examples) and the [Miden Bank Tutorial](../tutorials/miden-bank/index.md).
## Access control
@@ -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 42c5da51..d604e701 100644
--- a/docs/builder/smart-contracts/rust/index.md
+++ b/docs/builder/smart-contracts/rust/index.md
@@ -8,7 +8,7 @@ description: "Author Miden smart contracts in Rust — the long-term direction f
The Rust SDK is the long-term direction for Miden smart-contract development: define account components, note scripts, and transaction scripts in idiomatic `#![no_std]` Rust with typed storage, attribute macros, and client-side proving. The SDK compiles to Miden Assembly (MASM) under the hood, so the same execution model and standards library apply.
:::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.
@@ -25,13 +25,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.
@@ -40,16 +40,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 917328fd..0bf36d56 100644
--- a/docs/builder/smart-contracts/standards/account-components.md
+++ b/docs/builder/smart-contracts/standards/account-components.md
@@ -64,7 +64,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
@@ -95,6 +95,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 2cdb3b7c..70fdcaa2 100644
--- a/docs/builder/smart-contracts/standards/faucets-and-policies.md
+++ b/docs/builder/smart-contracts/standards/faucets-and-policies.md
@@ -118,7 +118,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
@@ -140,7 +140,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) - v0.14 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) - v0.14 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 a2c81327..d573f3db 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 788f3add..c86961cd 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;
@@ -101,7 +101,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 2d0725a5..0d22c4c3 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 77c7539b..f29576c0 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 f8bda2e1..fd16e880 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
@@ -94,11 +94,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 78ece75b..893a608f 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
@@ -283,7 +283,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..8433ef88 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 53864023..d7c60962 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/note-transport/index.md b/docs/builder/tools/note-transport/index.md
index 85bf4ead..ceac2551 100644
--- a/docs/builder/tools/note-transport/index.md
+++ b/docs/builder/tools/note-transport/index.md
@@ -14,13 +14,13 @@ Private note contents are not published onchain. The chain stores note commitmen
## Start here
-
+
How the node stores notes, assigns cursors, routes by tag, and handles current protocol boundaries.
-
+
CLI flags, Docker Compose, telemetry, storage, ports, retention, and production cautions.
-
+
Request and response shapes for send, fetch, stream, stats, plus the recommended client sync pattern.
diff --git a/docs/builder/tools/playground.md b/docs/builder/tools/playground.md
index 31b3449a..610fb1d9 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 505f0fc6..f3425f50 100644
--- a/docs/builder/tutorials/helpers/pitfalls.md
+++ b/docs/builder/tutorials/helpers/pitfalls.md
@@ -541,7 +541,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
@@ -550,7 +550,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)
:::
---
@@ -575,6 +575,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
-- **[Miden Bank Tutorial](../miden-bank/)** - See these patterns in context
+- **[Debugging Guide](./debugging.md)** - Troubleshoot errors
+- **[Testing Guide](./testing.md)** - MockChain patterns
+- **[Miden Bank Tutorial](../miden-bank/index.md)** - See these patterns in context
diff --git a/docs/builder/tutorials/helpers/testing.md b/docs/builder/tutorials/helpers/testing.md
index ff2bdc58..1acf54e1 100644
--- a/docs/builder/tutorials/helpers/testing.md
+++ b/docs/builder/tutorials/helpers/testing.md
@@ -605,6 +605,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
-- **[Miden Bank Tutorial](../miden-bank/)** - See testing in action
+- **[Debugging Guide](./debugging.md)** - Troubleshoot common issues
+- **[Common Pitfalls](./pitfalls.md)** - Avoid known gotchas
+- **[Miden Bank Tutorial](../miden-bank/index.md)** - See testing in action
diff --git a/docs/builder/tutorials/index.md b/docs/builder/tutorials/index.md
index 9d19467d..330ee4e6 100644
--- a/docs/builder/tutorials/index.md
+++ b/docs/builder/tutorials/index.md
@@ -11,7 +11,7 @@ Hands-on walkthroughs for building on Miden. Every tutorial pairs with runnable
## Pick a path
-
+
A 9-part curriculum — build a complete banking application covering components, storage, note scripts, cross-component calls, and output notes.
@@ -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}