From 4ecf2895491cb31f74fe6c78ba894d87505f9571 Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Tue, 12 Aug 2025 01:02:14 -0700 Subject: [PATCH 1/7] Update subnet memory capacity and threshold --- docs/building-apps/canister-management/resource-limits.mdx | 2 +- docs/building-apps/developer-tools/dfx-json.mdx | 2 +- docs/building-apps/developer-tools/dfx/dfx-json-schema.json | 2 +- docs/building-apps/essentials/gas-cost.mdx | 4 ++-- docs/building-apps/security/dos.mdx | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/building-apps/canister-management/resource-limits.mdx b/docs/building-apps/canister-management/resource-limits.mdx index a6ef5fb1ad..dfd71ad64f 100644 --- a/docs/building-apps/canister-management/resource-limits.mdx +++ b/docs/building-apps/canister-management/resource-limits.mdx @@ -36,7 +36,7 @@ The limits depend on the message type as shown in the following table. | Subnet limits                                                                        | Constraint  | | ------------------------------------------------------------------------------------ | ----------- | -| Subnet capacity (total memory available per subnet)                                  | 1TiB        | +| Subnet capacity (total memory available per subnet)                                  | 2TiB        | | Maximum number of snapshots per canister                                              | 1           | | Memory resource limits                                                               | Constraint  | diff --git a/docs/building-apps/developer-tools/dfx-json.mdx b/docs/building-apps/developer-tools/dfx-json.mdx index 782c583677..f2f07d5a34 100644 --- a/docs/building-apps/developer-tools/dfx-json.mdx +++ b/docs/building-apps/developer-tools/dfx-json.mdx @@ -191,7 +191,7 @@ Other fields applicable to all canister types include: - **`memory_allocation`** (`Byte`): Max memory (in bytes) for the canister. Can be integer or string with units (i.e. `72`, "2KB", or "4 MiB"). - - **`reserved_cycles_limit`** (`uint128 | null`): Upper limit of reserved cycles. Reserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB. + - **`reserved_cycles_limit`** (`uint128 | null`): Upper limit of reserved cycles. Reserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 750 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 750 GiB. - **`wasm_memory_limit`** (`Byte | null`): Specifies a soft limit (in bytes) on the Wasm memory usage of the canister.Update calls, timers, heartbeats, installs, and post-upgrades fail if the Wasm memory usage exceeds this limit. The main purpose of this setting is to protect against the case when the canister reaches the hard 4GiB limit. Must be a number of bytes between 0 and 2^48 (i.e. 256 TiB), inclusive. Can be specified as an integer, or as an SI unit string (e.g. "4KB", "2 MiB"). diff --git a/docs/building-apps/developer-tools/dfx/dfx-json-schema.json b/docs/building-apps/developer-tools/dfx/dfx-json-schema.json index 6f6fdc505f..de89bb9b0b 100644 --- a/docs/building-apps/developer-tools/dfx/dfx-json-schema.json +++ b/docs/building-apps/developer-tools/dfx/dfx-json-schema.json @@ -1010,7 +1010,7 @@ }, "reserved_cycles_limit": { "title": "Reserved Cycles Limit", - "description": "Specifies the upper limit of the canister's reserved cycles balance.\n\nReserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 450 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 450 GiB.", + "description": "Specifies the upper limit of the canister's reserved cycles balance.\n\nReserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 750 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 750 GiB.", "default": null, "type": [ "integer", diff --git a/docs/building-apps/essentials/gas-cost.mdx b/docs/building-apps/essentials/gas-cost.mdx index 0e5aca1eaf..771795af40 100644 --- a/docs/building-apps/essentials/gas-cost.mdx +++ b/docs/building-apps/essentials/gas-cost.mdx @@ -119,9 +119,9 @@ Operations that potentially allocate new storage bytes are: The reserved cycles are not transferable, and the amount depends on how full the subnet’s memory is. -- If subnet usage is below `450GiB`, then the amount of reserved cycles per allocated byte is `0`. +- If subnet usage is below `750GiB`, then the amount of reserved cycles per allocated byte is `0`. -- If subnet usage is above `450GiB`, then the amount of reserved cycles per allocated byte grows linearly depending on the subnet usage, from `0` to `10` years worth of payments at the subnet capacity (`1TiB`). +- If subnet usage is above `750GiB`, then the amount of reserved cycles per allocated byte grows linearly depending on the subnet usage, from `0` to `10` years worth of payments at the subnet capacity (`2TiB`). A controller of a canister can disable resource reservation by setting the `reserved_cycles_limit=0` in the canister’s settings. Such opted-out canisters are not able to allocate if the subnet usage is above `450GiB`. diff --git a/docs/building-apps/security/dos.mdx b/docs/building-apps/security/dos.mdx index 52190de00c..267eb4c514 100644 --- a/docs/building-apps/security/dos.mdx +++ b/docs/building-apps/security/dos.mdx @@ -37,7 +37,7 @@ Learn more about managing memory and compute resources in the [storage](/docs/bu * **Subnet and canister distribution**: Implement a smart canister deployment strategy by monitoring the load on subnets. You can choose to deploy new canisters on less busy subnets or adopt a multi-canister architecture that balances the load across subnets. Be mindful to minimize inter-subnet communication for canisters that frequently interact with each other. Additionally, avoid deploying to known high-traffic subnets where possible, though keep in mind that resource usage can change unexpectedly with new dapps. :::info -When the subnet grows above 450GiB, then the new reservation mechanism activates. Every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable, and the amount of reserved cycles depends on how full the subnet is. For example, it may cover days, months, or even years of payments for the newly allocated bytes. It is important to note that the reservation mechanism applies only to the newly allocated bytes and does not apply to the storage already in use by the canister. See more at [resource reservations](https://forum.dfinity.org/t/increasing-subnet-storage-capacity-and-introducing-resource-reservation-mechanism/23447). +When the subnet grows above 750GiB, then the new reservation mechanism activates. Every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable, and the amount of reserved cycles depends on how full the subnet is. For example, it may cover days, months, or even years of payments for the newly allocated bytes. It is important to note that the reservation mechanism applies only to the newly allocated bytes and does not apply to the storage already in use by the canister. See more at [resource reservations](https://forum.dfinity.org/t/increasing-subnet-storage-capacity-and-introducing-resource-reservation-mechanism/23447). ::: ## Handle expensive calls @@ -60,4 +60,4 @@ An attacker will target expensive calls to drain the cycles balance or available - Automatically monitor cycles consumption and set appropriate alerts for cycles consumption rate and balance. Sudden spikes in cycles consumption could indicate an attack. - Implement early authentication and rate limiting for your canisters. - Be aware of attacks targeting high cycles-consuming calls. -- See the "Cycle balance drain attacks section" in [How to audit an ICP canister](https://www.joachim-breitner.de/blog/788-How_to_audit_an_Internet_Computer_canister). \ No newline at end of file +- See the "Cycle balance drain attacks section" in [How to audit an ICP canister](https://www.joachim-breitner.de/blog/788-How_to_audit_an_Internet_Computer_canister). From 3b9df96b8cad5801dbd003eb3c3678d28cb47dbc Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 17:12:40 -0700 Subject: [PATCH 2/7] constant --- docusaurus.config.js | 2 + plugins/constants-replacer/index.js | 80 +++++++++++++++++++++++++++++ plugins/remark/constants.js | 45 ++++++++++++++++ site-constants.json | 6 +++ 4 files changed, 133 insertions(+) create mode 100644 plugins/constants-replacer/index.js create mode 100644 plugins/remark/constants.js create mode 100644 site-constants.json diff --git a/docusaurus.config.js b/docusaurus.config.js index 2fb5a86120..715627d7c3 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -43,6 +43,7 @@ const remarkPlugins = [ simplePlantUML, require("remark-code-import"), require("./plugins/remark/validate-links.js"), + [require("./plugins/remark/constants.js"), { constantsPath: "site-constants.json" }], ]; const rehypePlugins = [katex]; @@ -641,6 +642,7 @@ const config = { snsDataPlugin, airtablePlugin, youtubePlugin, + [require.resolve("./plugins/constants-replacer"), { constantsPath: "site-constants.json" }], validateShowcasePlugin, externalRedirectsPlugin({ redirects: [...getExternalRedirects(), ...getExactUrlRedirects()], diff --git a/plugins/constants-replacer/index.js b/plugins/constants-replacer/index.js new file mode 100644 index 0000000000..a959714f1c --- /dev/null +++ b/plugins/constants-replacer/index.js @@ -0,0 +1,80 @@ +const fs = require("fs"); +const path = require("path"); + +function readConstants(constantsPath) { + const resolvedPath = path.isAbsolute(constantsPath) + ? constantsPath + : path.join(process.cwd(), constantsPath); + const raw = fs.readFileSync(resolvedPath, "utf8"); + return JSON.parse(raw); +} + +function buildTokenRegex(constantsKeys) { + const escapedKeys = constantsKeys.map((k) => k.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")); + return new RegExp(`\\\\{\\\\{(${escapedKeys.join("|")})\\\\}\\\\}`, "g"); +} + +function listFilesRecursive(dir, shouldProcessFile) { + const results = []; + const entries = fs.readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const fullPath = path.join(dir, entry.name); + if (entry.isDirectory()) { + results.push(...listFilesRecursive(fullPath, shouldProcessFile)); + } else if (shouldProcessFile(fullPath)) { + results.push(fullPath); + } + } + return results; +} + +module.exports = function constantsReplacerPlugin(context, options) { + const constantsPath = options?.constantsPath || "site-constants.json"; + const includeExtensions = options?.includeExtensions || [ + ".html", + ".js", + ".css", + ".json", + ".xml", + ".txt", + ]; + const excludePaths = options?.excludePaths || [ + "/img/", + "/fonts/", + "/assets/", + "/static/", + ]; + + return { + name: "constants-replacer", + async postBuild({ outDir }) { + const constants = readConstants(constantsPath); + const keys = Object.keys(constants); + if (keys.length === 0) return; + const tokenRegex = buildTokenRegex(keys); + + const shouldProcessFile = (filePath) => { + const rel = path.relative(outDir, filePath); + if (!includeExtensions.includes(path.extname(filePath))) return false; + return !excludePaths.some((p) => rel.includes(p)); + }; + + const files = listFilesRecursive(outDir, shouldProcessFile); + for (const file of files) { + try { + const content = fs.readFileSync(file, "utf8"); + if (!tokenRegex.test(content)) continue; + const replaced = content.replace(tokenRegex, (_, key) => String(constants[key] ?? "")); + if (replaced !== content) { + fs.writeFileSync(file, replaced, "utf8"); + } + } catch (err) { + console.warn(`[constants-replacer] Failed to process ${file}:`, err.message); + } + } + }, + }; +}; + + + diff --git a/plugins/remark/constants.js b/plugins/remark/constants.js new file mode 100644 index 0000000000..d472c29fce --- /dev/null +++ b/plugins/remark/constants.js @@ -0,0 +1,45 @@ +const fs = require("fs"); +const path = require("path"); + +function readConstants(constantsPath) { + const resolvedPath = path.isAbsolute(constantsPath) + ? constantsPath + : path.join(process.cwd(), constantsPath); + const raw = fs.readFileSync(resolvedPath, "utf8"); + return JSON.parse(raw); +} + +function buildTokenRegex(constantsKeys) { + const escapedKeys = constantsKeys.map((k) => k.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&")); + return new RegExp(`\\\\{\\\\{(${escapedKeys.join("|")})\\\\}\\\\}`, "g"); +} + +module.exports = function remarkConstants(options = {}) { + const constantsPath = options.constantsPath || "site-constants.json"; + const constants = readConstants(constantsPath); + const keys = Object.keys(constants); + if (keys.length === 0) return () => {}; + const tokenRegex = buildTokenRegex(keys); + + function replaceInValue(value) { + if (typeof value !== "string" || value.length === 0) return value; + return value.replace(tokenRegex, (_, key) => String(constants[key] ?? "")); + } + + return function transformer(tree) { + visitNodes(tree, (node) => { + if (node.type === "text" || node.type === "html" || node.type === "inlineCode" || node.type === "code") { + node.value = replaceInValue(node.value); + } + }); + }; +}; + +function visitNodes(node, visitor) { + visitor(node); + const children = node.children || []; + for (const child of children) visitNodes(child, visitor); +} + + + diff --git a/site-constants.json b/site-constants.json new file mode 100644 index 0000000000..3b850a3fa0 --- /dev/null +++ b/site-constants.json @@ -0,0 +1,6 @@ +{ + "SUBNET_CAPACITY_TIB": "2TiB", + "SUBNET_THRESHOLD_GIB": "750GiB" +} + + From 1790bdf68c633ec4c896d9c35e8099a2d73a6a6a Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 17:35:46 -0700 Subject: [PATCH 3/7] move to building-apps --- .../building-apps/site-constants.json | 1 - docusaurus.config.js | 4 ++-- plugins/constants-replacer/index.js | 4 +++- plugins/remark/constants.js | 7 ++++++- 4 files changed, 11 insertions(+), 5 deletions(-) rename site-constants.json => docs/building-apps/site-constants.json (98%) diff --git a/site-constants.json b/docs/building-apps/site-constants.json similarity index 98% rename from site-constants.json rename to docs/building-apps/site-constants.json index 3b850a3fa0..3c10cc6d81 100644 --- a/site-constants.json +++ b/docs/building-apps/site-constants.json @@ -3,4 +3,3 @@ "SUBNET_THRESHOLD_GIB": "750GiB" } - diff --git a/docusaurus.config.js b/docusaurus.config.js index 715627d7c3..7ebab2fa77 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -43,7 +43,7 @@ const remarkPlugins = [ simplePlantUML, require("remark-code-import"), require("./plugins/remark/validate-links.js"), - [require("./plugins/remark/constants.js"), { constantsPath: "site-constants.json" }], + [require("./plugins/remark/constants.js"), { constantsPath: "docs/building-apps/site-constants.json" }], ]; const rehypePlugins = [katex]; @@ -642,7 +642,7 @@ const config = { snsDataPlugin, airtablePlugin, youtubePlugin, - [require.resolve("./plugins/constants-replacer"), { constantsPath: "site-constants.json" }], + [require.resolve("./plugins/constants-replacer"), { constantsPath: "docs/building-apps/site-constants.json" }], validateShowcasePlugin, externalRedirectsPlugin({ redirects: [...getExternalRedirects(), ...getExactUrlRedirects()], diff --git a/plugins/constants-replacer/index.js b/plugins/constants-replacer/index.js index a959714f1c..f75ba7577a 100644 --- a/plugins/constants-replacer/index.js +++ b/plugins/constants-replacer/index.js @@ -28,8 +28,10 @@ function listFilesRecursive(dir, shouldProcessFile) { return results; } + module.exports = function constantsReplacerPlugin(context, options) { const constantsPath = options?.constantsPath || "site-constants.json"; + const includeBuildSubdir = options?.includeBuildSubdir || "docs/building-apps"; // only process this subtree under outDir const includeExtensions = options?.includeExtensions || [ ".html", ".js", @@ -41,7 +43,6 @@ module.exports = function constantsReplacerPlugin(context, options) { const excludePaths = options?.excludePaths || [ "/img/", "/fonts/", - "/assets/", "/static/", ]; @@ -55,6 +56,7 @@ module.exports = function constantsReplacerPlugin(context, options) { const shouldProcessFile = (filePath) => { const rel = path.relative(outDir, filePath); + if (!rel.startsWith(includeBuildSubdir)) return false; // outside target subtree if (!includeExtensions.includes(path.extname(filePath))) return false; return !excludePaths.some((p) => rel.includes(p)); }; diff --git a/plugins/remark/constants.js b/plugins/remark/constants.js index d472c29fce..97036d1e69 100644 --- a/plugins/remark/constants.js +++ b/plugins/remark/constants.js @@ -16,6 +16,7 @@ function buildTokenRegex(constantsKeys) { module.exports = function remarkConstants(options = {}) { const constantsPath = options.constantsPath || "site-constants.json"; + const includePathPrefix = options.includePathPrefix || "docs/building-apps/"; const constants = readConstants(constantsPath); const keys = Object.keys(constants); if (keys.length === 0) return () => {}; @@ -26,7 +27,11 @@ module.exports = function remarkConstants(options = {}) { return value.replace(tokenRegex, (_, key) => String(constants[key] ?? "")); } - return function transformer(tree) { + return function transformer(tree, file) { + const filePath = file && (file.path || (file.history && file.history[0])); + if (filePath && !filePath.includes(includePathPrefix)) { + return; // skip files outside the allowed area + } visitNodes(tree, (node) => { if (node.type === "text" || node.type === "html" || node.type === "inlineCode" || node.type === "code") { node.value = replaceInValue(node.value); From e50370dbefbce6654f116c21864644daa334b6c0 Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 17:58:09 -0700 Subject: [PATCH 4/7] use constant --- docs/building-apps/canister-management/resource-limits.mdx | 2 +- docs/building-apps/developer-tools/dfx-json.mdx | 2 +- docs/building-apps/developer-tools/dfx/dfx-json-schema.json | 2 +- docs/building-apps/essentials/gas-cost.mdx | 6 +++--- docs/building-apps/security/dos.mdx | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/building-apps/canister-management/resource-limits.mdx b/docs/building-apps/canister-management/resource-limits.mdx index dfd71ad64f..91dcd34401 100644 --- a/docs/building-apps/canister-management/resource-limits.mdx +++ b/docs/building-apps/canister-management/resource-limits.mdx @@ -36,7 +36,7 @@ The limits depend on the message type as shown in the following table. | Subnet limits                                                                        | Constraint  | | ------------------------------------------------------------------------------------ | ----------- | -| Subnet capacity (total memory available per subnet)                                  | 2TiB        | +| Subnet capacity (total memory available per subnet)                                  | {{SUBNET_CAPACITY_TIB}}        | | Maximum number of snapshots per canister                                              | 1           | | Memory resource limits                                                               | Constraint  | diff --git a/docs/building-apps/developer-tools/dfx-json.mdx b/docs/building-apps/developer-tools/dfx-json.mdx index f2f07d5a34..2577a4b028 100644 --- a/docs/building-apps/developer-tools/dfx-json.mdx +++ b/docs/building-apps/developer-tools/dfx-json.mdx @@ -191,7 +191,7 @@ Other fields applicable to all canister types include: - **`memory_allocation`** (`Byte`): Max memory (in bytes) for the canister. Can be integer or string with units (i.e. `72`, "2KB", or "4 MiB"). - - **`reserved_cycles_limit`** (`uint128 | null`): Upper limit of reserved cycles. Reserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 750 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 750 GiB. + - **`reserved_cycles_limit`** (`uint128 | null`): Upper limit of reserved cycles. Reserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds {{SUBNET_THRESHOLD_GIB}}, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds {{SUBNET_THRESHOLD_GIB}}. - **`wasm_memory_limit`** (`Byte | null`): Specifies a soft limit (in bytes) on the Wasm memory usage of the canister.Update calls, timers, heartbeats, installs, and post-upgrades fail if the Wasm memory usage exceeds this limit. The main purpose of this setting is to protect against the case when the canister reaches the hard 4GiB limit. Must be a number of bytes between 0 and 2^48 (i.e. 256 TiB), inclusive. Can be specified as an integer, or as an SI unit string (e.g. "4KB", "2 MiB"). diff --git a/docs/building-apps/developer-tools/dfx/dfx-json-schema.json b/docs/building-apps/developer-tools/dfx/dfx-json-schema.json index de89bb9b0b..5742e13be0 100644 --- a/docs/building-apps/developer-tools/dfx/dfx-json-schema.json +++ b/docs/building-apps/developer-tools/dfx/dfx-json-schema.json @@ -1010,7 +1010,7 @@ }, "reserved_cycles_limit": { "title": "Reserved Cycles Limit", - "description": "Specifies the upper limit of the canister's reserved cycles balance.\n\nReserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds 750 GiB, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds 750 GiB.", + "description": "Specifies the upper limit of the canister's reserved cycles balance.\n\nReserved cycles are cycles that the system sets aside for future use by the canister. If a subnet's storage exceeds {{SUBNET_THRESHOLD_GIB}}, then every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable and the amount of reserved cycles depends on how full the subnet is.\n\nA setting of 0 means that the canister will trap if it tries to allocate new storage while the subnet's memory usage exceeds {{SUBNET_THRESHOLD_GIB}}.", "default": null, "type": [ "integer", diff --git a/docs/building-apps/essentials/gas-cost.mdx b/docs/building-apps/essentials/gas-cost.mdx index 771795af40..7af6e480b7 100644 --- a/docs/building-apps/essentials/gas-cost.mdx +++ b/docs/building-apps/essentials/gas-cost.mdx @@ -119,11 +119,11 @@ Operations that potentially allocate new storage bytes are: The reserved cycles are not transferable, and the amount depends on how full the subnet’s memory is. -- If subnet usage is below `750GiB`, then the amount of reserved cycles per allocated byte is `0`. +- If subnet usage is below `{{SUBNET_THRESHOLD_GIB}}`, then the amount of reserved cycles per allocated byte is `0`. -- If subnet usage is above `750GiB`, then the amount of reserved cycles per allocated byte grows linearly depending on the subnet usage, from `0` to `10` years worth of payments at the subnet capacity (`2TiB`). +- If subnet usage is above `{{SUBNET_THRESHOLD_GIB}}`, then the amount of reserved cycles per allocated byte grows linearly depending on the subnet usage, from `0` to `10` years worth of payments at the subnet capacity (`{{SUBNET_CAPACITY_TIB}}`). -A controller of a canister can disable resource reservation by setting the `reserved_cycles_limit=0` in the canister’s settings. Such opted-out canisters are not able to allocate if the subnet usage is above `450GiB`. +A controller of a canister can disable resource reservation by setting the `reserved_cycles_limit=0` in the canister’s settings. Such opted-out canisters are not able to allocate if the subnet usage is above `{{SUBNET_THRESHOLD_GIB}}`. To prevent repeated allocations resulting in repeated increases of the canister’s reserved cycles balance, a memory allocation can be specified in the canister settings. This way, the canister will be charged as if the entire amount of allocated storage is being used, but no additional allocations will be performed. Hence, the canister’s reserved cycles balance might only increase when setting the memory allocation, but does not increase afterwards. diff --git a/docs/building-apps/security/dos.mdx b/docs/building-apps/security/dos.mdx index 267eb4c514..50525d365d 100644 --- a/docs/building-apps/security/dos.mdx +++ b/docs/building-apps/security/dos.mdx @@ -37,7 +37,7 @@ Learn more about managing memory and compute resources in the [storage](/docs/bu * **Subnet and canister distribution**: Implement a smart canister deployment strategy by monitoring the load on subnets. You can choose to deploy new canisters on less busy subnets or adopt a multi-canister architecture that balances the load across subnets. Be mindful to minimize inter-subnet communication for canisters that frequently interact with each other. Additionally, avoid deploying to known high-traffic subnets where possible, though keep in mind that resource usage can change unexpectedly with new dapps. :::info -When the subnet grows above 750GiB, then the new reservation mechanism activates. Every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable, and the amount of reserved cycles depends on how full the subnet is. For example, it may cover days, months, or even years of payments for the newly allocated bytes. It is important to note that the reservation mechanism applies only to the newly allocated bytes and does not apply to the storage already in use by the canister. See more at [resource reservations](https://forum.dfinity.org/t/increasing-subnet-storage-capacity-and-introducing-resource-reservation-mechanism/23447). +When the subnet grows above {{SUBNET_THRESHOLD_GIB}}, then the new reservation mechanism activates. Every time a canister allocates new storage bytes, the system sets aside some amount of cycles from the main balance of the canister. These reserved cycles will be used to cover future payments for the newly allocated bytes. The reserved cycles are not transferable, and the amount of reserved cycles depends on how full the subnet is. For example, it may cover days, months, or even years of payments for the newly allocated bytes. It is important to note that the reservation mechanism applies only to the newly allocated bytes and does not apply to the storage already in use by the canister. See more at [resource reservations](https://forum.dfinity.org/t/increasing-subnet-storage-capacity-and-introducing-resource-reservation-mechanism/23447). ::: ## Handle expensive calls From 6b427d721fde78a0f29738ca169324bdb6bc75d5 Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 18:30:56 -0700 Subject: [PATCH 5/7] fix --- plugins/remark/constants.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/plugins/remark/constants.js b/plugins/remark/constants.js index 97036d1e69..9b3b06e47e 100644 --- a/plugins/remark/constants.js +++ b/plugins/remark/constants.js @@ -36,6 +36,20 @@ module.exports = function remarkConstants(options = {}) { if (node.type === "text" || node.type === "html" || node.type === "inlineCode" || node.type === "code") { node.value = replaceInValue(node.value); } + // Replace in MDX JSX element attributes as well + if (node.type === "mdxJsxFlowElement" || node.type === "mdxJsxTextElement") { + if (Array.isArray(node.attributes)) { + for (const attr of node.attributes) { + if (attr && attr.type === "mdxJsxAttribute") { + if (typeof attr.value === "string") { + attr.value = replaceInValue(attr.value); + } else if (attr.value && typeof attr.value.value === "string") { + attr.value.value = replaceInValue(attr.value.value); + } + } + } + } + } }); }; }; From 35c4e996d099170109bcb8199387306d462b4a3c Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 18:44:49 -0700 Subject: [PATCH 6/7] fix --- plugins/constants-replacer/index.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/constants-replacer/index.js b/plugins/constants-replacer/index.js index f75ba7577a..1b1d526d20 100644 --- a/plugins/constants-replacer/index.js +++ b/plugins/constants-replacer/index.js @@ -31,7 +31,8 @@ function listFilesRecursive(dir, shouldProcessFile) { module.exports = function constantsReplacerPlugin(context, options) { const constantsPath = options?.constantsPath || "site-constants.json"; - const includeBuildSubdir = options?.includeBuildSubdir || "docs/building-apps"; // only process this subtree under outDir + // Only process built routes for building-apps, including versioned paths + const includeBuildSubdir = options?.includeBuildSubdir || "docs"; const includeExtensions = options?.includeExtensions || [ ".html", ".js", @@ -56,7 +57,13 @@ module.exports = function constantsReplacerPlugin(context, options) { const shouldProcessFile = (filePath) => { const rel = path.relative(outDir, filePath); - if (!rel.startsWith(includeBuildSubdir)) return false; // outside target subtree + if (!rel.startsWith(includeBuildSubdir)) return false; // outside /docs + // Allow only these route patterns within /docs + const inBuildingApps = + rel.startsWith("docs/building-apps/") || + rel.startsWith("docs/next/building-apps/") || + rel.includes("/docs/version-") && rel.includes("/building-apps/"); + if (!inBuildingApps) return false; if (!includeExtensions.includes(path.extname(filePath))) return false; return !excludePaths.some((p) => rel.includes(p)); }; From 9b176ca3c5bd2b1fcc81e3562445c4f52614ef9b Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Fri, 29 Aug 2025 18:53:51 -0700 Subject: [PATCH 7/7] fix --- docusaurus.config.js | 1 - plugins/remark/constants.js | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 7ebab2fa77..7da9ffd6fa 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -642,7 +642,6 @@ const config = { snsDataPlugin, airtablePlugin, youtubePlugin, - [require.resolve("./plugins/constants-replacer"), { constantsPath: "docs/building-apps/site-constants.json" }], validateShowcasePlugin, externalRedirectsPlugin({ redirects: [...getExternalRedirects(), ...getExactUrlRedirects()], diff --git a/plugins/remark/constants.js b/plugins/remark/constants.js index 9b3b06e47e..9bfd184c22 100644 --- a/plugins/remark/constants.js +++ b/plugins/remark/constants.js @@ -29,7 +29,9 @@ module.exports = function remarkConstants(options = {}) { return function transformer(tree, file) { const filePath = file && (file.path || (file.history && file.history[0])); - if (filePath && !filePath.includes(includePathPrefix)) { + const isVersioned = filePath && filePath.includes("versioned_docs") && filePath.includes("/building-apps/"); + const isCurrent = filePath && filePath.includes(includePathPrefix); + if (filePath && !(isCurrent || isVersioned)) { return; // skip files outside the allowed area } visitNodes(tree, (node) => {