diff --git a/documentation/docs/components/landing-page.tsx b/documentation/docs/components/landing-page.tsx index 699e704263..011ab24266 100644 --- a/documentation/docs/components/landing-page.tsx +++ b/documentation/docs/components/landing-page.tsx @@ -1,120 +1,504 @@ -import React from "react"; -import { Box, Grid, Typography } from "@mui/material"; -import useMediaQuery from "@mui/material/useMediaQuery"; -import { useTheme } from "@mui/material/styles"; - -import Image from "next/image"; +import React, { useState, useEffect, useRef } from "react"; import Link from "next/link"; -import networkDocs from "../public/images/landing/Vector1.png"; -import developerDocs from "../public/images/landing/Vector2.png"; -import sdkDocs from "../public/images/landing/Vector3.png"; -import operatorGuide from "../public/images/landing/Vector4.png"; -export const LandingPage = () => { - const theme = useTheme(); - const isTablet = useMediaQuery(theme.breakpoints.up("md")); - const isDesktop = useMediaQuery(theme.breakpoints.up("xl")); +const asciiStyle: React.CSSProperties = { + fontFamily: "var(--font-mono)", + fontSize: "0.72rem", + lineHeight: 1.4, + color: "var(--colorPrimary)", + opacity: 0.7, + whiteSpace: "pre", + margin: 0, +}; - const squares = [ - { - text: "Network Docs", - description: "Architecture, crypto systems, and how the Mixnet works", - href: "/network", - icon: developerDocs, - }, - { - text: "Operator Guides", - description: - "Guides and maintenance: if you want to run a node, start here", +// ── Animation components ── - href: "/operators/introduction", - icon: operatorGuide, - }, - { - text: "Developer Portal", - description: "Conceptual overview, clients, tools and SDKs", +const randomRow = () => Math.floor(Math.random() * 3); +const randomPath = () => [randomRow(), randomRow(), randomRow()]; - href: "/developers", - icon: sdkDocs, - }, - { - text: "APIs", - description: "Interactive API specs for Nym infrastructure", +const NetworkAnimation = () => { + // Packets traverse 5 stages: gw_e(0) → M1(1) → M2(2) → M3(3) → gw_ex(4) + // stage -1 = not yet mounted (SSR-safe, renders all ○) + const [packets, setPackets] = useState([ + { path: randomPath(), stage: -1 }, + { path: randomPath(), stage: -1 }, + ]); + useEffect(() => { + // kick off with staggered positions + setPackets([ + { path: randomPath(), stage: 0 }, + { path: randomPath(), stage: 3 }, + ]); - href: "/apis/introduction", - icon: networkDocs, - }, - ]; + const id = setInterval(() => { + setPackets((prev) => + prev.map((p) => { + const next = (p.stage + 1) % 5; + return { stage: next, path: next === 0 ? randomPath() : p.path }; + }) + ); + }, 300); + return () => clearInterval(id); + }, []); - const shortenDescription = (description: string) => { - return description.slice(0, 18) + "..."; + const gwNode = (stage: number) => { + const active = packets.some((p) => p.stage === stage); + return ( + + {active ? "\u25CF" : "\u25CB"} + + ); + }; + + const mixNode = (col: number, row: number) => { + const active = packets.some( + (p) => p.stage === col + 1 && p.path[col] === row + ); + const filled = active; + return ( + + {filled ? "\u25CF" : "\u25CB"} + + ); }; return ( - - {/* - Nym Docs - - - - Nym is a privacy platform. It provides strong network-level privacy - against sophisticated end-to-end attackers, and anonymous access control - using blinded, re-randomizable, decentralized credentials. Our goal is - to allow developers to build new applications, or upgrade existing apps, - with privacy features unavailable in other systems. - */} - - {squares.map((square, index) => ( - - - - - {square.text} - - - - {square.description} - - - {square.text} - - - - ))} - - +
+      {"gw_e  M1   M2   M3  gw_ex\n"}
+      {"       "}
+      {mixNode(0, 0)}
+      {" \u2500\u2500 "}
+      {mixNode(1, 0)}
+      {" \u2500\u2500 "}
+      {mixNode(2, 0)}
+      {"\n"}
+      {"        \\  / \\  /\n"}
+      {"  "}
+      {gwNode(0)}
+      {" \u2500\u2500 "}
+      {mixNode(0, 1)}
+      {" \u2500\u2500 "}
+      {mixNode(1, 1)}
+      {" \u2500\u2500 "}
+      {mixNode(2, 1)}
+      {" \u2500\u2500 "}
+      {gwNode(4)}
+      {"\n"}
+      {"        /  \\ /  \\\n"}
+      {"       "}
+      {mixNode(0, 2)}
+      {" \u2500\u2500 "}
+      {mixNode(1, 2)}
+      {" \u2500\u2500 "}
+      {mixNode(2, 2)}
+    
+ ); +}; + +const TypewriterAnimation = () => { + const text = + "let client = MixnetClient::connect_new().await?;\n" + + "\n" + + "client.send(msg).await;"; + const [charCount, setCharCount] = useState(0); + const [showCursor, setShowCursor] = useState(true); + + useEffect(() => { + let cancelled = false; + const run = () => { + setCharCount(0); + let i = 0; + const type = () => { + if (cancelled) return; + if (i <= text.length) { + setCharCount(i); + i++; + setTimeout(type, 40); + } else { + setTimeout(() => { + if (!cancelled) run(); + }, 2000); + } + }; + type(); + }; + run(); + return () => { + cancelled = true; + }; + }, []); + + useEffect(() => { + const id = setInterval(() => setShowCursor((v) => !v), 530); + return () => clearInterval(id); + }, []); + + return ( +
+      {text.slice(0, charCount)}
+      {showCursor ? "\u258C" : " "}
+      {text.slice(charCount)}
+    
+ ); +}; + +const OperatorsAnimation = () => { + const totalBars = 10; + const [tick, setTick] = useState(0); + const mixRef = useRef(0); + const [mixCount, setMixCount] = useState(0); + + useEffect(() => { + const id = setInterval(() => { + setTick((t) => t + 1); + mixRef.current += Math.floor(Math.random() * 8) + 5; + setMixCount(mixRef.current); + }, 80); + return () => clearInterval(id); + }, []); + + const mixFilled = Math.min(tick % 12, totalBars); + const bar = (f: number) => + "\u25A0".repeat(f) + "\u25A1".repeat(totalBars - f); + const fmt = (n: number) => n.toLocaleString("en"); + + return ( +
+      {"> nym-node run\n\n"}
+      {"  mixing: "}
+      {bar(mixFilled)}
+      {"  "}
+      {fmt(mixCount)}
+      {" pkts"}
+    
+ ); +}; + +const ApiAnimation = () => { + const lines = [ + "GET /v1/mixnodes/active", + "", + '{ "count": 498,', + ' "nodes": [ ... ] }', + ]; + const [visibleLines, setVisibleLines] = useState(0); + + useEffect(() => { + let cancelled = false; + const run = () => { + setVisibleLines(0); + setTimeout(() => { + if (cancelled) return; + setVisibleLines(1); + setTimeout(() => { + if (cancelled) return; + let i = 2; + const reveal = () => { + if (cancelled) return; + if (i <= lines.length) { + setVisibleLines(i); + i++; + setTimeout(reveal, 300); + } else { + setTimeout(() => { + if (!cancelled) run(); + }, 2000); + } + }; + reveal(); + }, 800); + }, 100); + }; + run(); + return () => { + cancelled = true; + }; + }, []); + + return ( +
+      {lines.slice(0, visibleLines).map((line, i) => (
+        
+          {i > 0 && "\n"}
+          {line}
+        
+      ))}
+      
+        {lines.slice(visibleLines).map((line, i) => (
+          
+            {visibleLines > 0 || i > 0 ? "\n" : ""}
+            {line}
+          
+        ))}
+      
+    
+ ); +}; + +// ── Section data ── + +const sections = [ + { + title: "Network", + description: + "Architecture, cryptographic systems, and how the Mixnet protects your traffic.", + href: "/network", + animation: "network" as const, + }, + { + title: "Developers", + description: "SDKs, tutorials, and integration guides for building on Nym.", + href: "/developers", + animation: "typewriter" as const, + }, + { + title: "Operators", + description: + "Set up and maintain mix nodes, gateways, and network infrastructure.", + href: "/operators/introduction", + animation: "progress" as const, + }, + { + title: "APIs", + description: "Interactive specs for querying Nym infrastructure.", + href: "/apis/introduction", + animation: "api" as const, + }, +]; + +const AnimationBlock = ({ type }: { type: string }) => { + switch (type) { + case "network": + return ; + case "typewriter": + return ; + case "progress": + return ; + case "api": + return ; + default: + return null; + } +}; + +const sdks = [ + { + name: "Rust", + description: + "Native SDK with async Mixnet client, streams, and TcpProxy modules.", + href: "/developers/rust", + }, + { + name: "TypeScript", + description: + "Browser-based SDK with fetch API replacement and message-based WebSocket transport.", + href: "/developers/typescript", + }, +]; + +export const LandingPage = () => { + return ( +
+ {/* ── Section cards ── */} +
+ {sections.map((s, i) => ( + +
+
+
+

+ {s.title} +

+ + › + +
+

+ {s.description} +

+
+ +
+ + ))} +
+ + {/* ── SDKs ── */} +
+
+

+ SDKs +

+

+ Integrate Mixnet privacy into your application with our Rust and + TypeScript SDKs. +

+
+
+ {sdks.map((sdk, i) => ( + +
0 ? "-1px" : undefined, + display: "flex", + alignItems: "center", + justifyContent: "space-between", + transition: "background-color 0.15s", + cursor: "pointer", + }} + > +
+ + {sdk.name} + +

+ {sdk.description} +

+
+ + › + +
+ + ))} +
+
+ + {/* ── Links ── */} + +
); }; diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md index d0e186efdb..597096bfa2 100644 --- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md +++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md @@ -1 +1 @@ -Wednesday, March 18th 2026, 14:02:49 UTC +Tuesday, March 24th 2026, 13:05:57 UTC diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md index f10b058c61..b3e26b1f56 100644 --- a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md +++ b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md @@ -4,109 +4,86 @@ Start this nym-node Usage: nym-node run [OPTIONS] Options: - --id - Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] - --config-file - Path to a configuration file of this node [env: NYMNODE_CONFIG=] - --accept-operator-terms-and-conditions - Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at [env: - NYMNODE_ACCEPT_OPERATOR_TERMS=] - --deny-init - Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=] - --init-only - If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=] - --local - Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=] - --mode [...] - Specifies the current mode(s) of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only] - --modes - Specifies the current mode(s) of this nym-node as a single flag [env: NYMNODE_MODES=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only] - -w, --write-changes - If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=] - --bonding-information-output - Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and this argument should be - treated only as a preview of future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=] - -o, --output - Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible values: text, json] - --public-ips - Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In nearly all circumstances, it's going to be identical to the - address you're going to use for bonding [env: NYMNODE_PUBLIC_IPS=] - --hostname - Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: NYMNODE_HOSTNAME=] - --location - Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit - numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=] - --http-bind-address - Socket address this node will use for binding its http API. default: `[::]:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=] - --landing-page-assets-path - Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=] - --http-access-token - An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: NYMNODE_HTTP_ACCESS_TOKEN=] - --expose-system-info - Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] [possible values: true, false] - --expose-system-hardware - Specify whether basic system hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false] - --expose-crypto-hardware - Specify whether detailed system crypto hardware information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false] - --mixnet-bind-address - Address this node will bind to for listening for mixnet packets default: `[::]:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=] - --mixnet-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=] - --nym-api-urls - Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=] - --nyxd-urls - Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=] - --enable-console-logging - Specify whether running statistics of this node should be logged to the console [env: NYMNODE_ENABLE_CONSOLE_LOGGING=] [possible values: true, false] - --wireguard-enabled - Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] - --wireguard-bind-address - Socket address this node will use for binding its wireguard interface. default: `[::]:51822` [env: NYMNODE_WG_BIND_ADDRESS=] - --wireguard-tunnel-announced-port - Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] - --wireguard-private-network-prefix - The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is 32 and for IPv6 is 128 [env: - NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] - --wireguard-userspace - Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments without kernel WireGuard support [env: NYMNODE_WG_USERSPACE=] - [possible values: true, false] - --verloc-bind-address - Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] - --verloc-announce-port - If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=] - --entry-bind-address - Socket address this node will use for binding its client websocket API. default: `[::]:9000` [env: NYMNODE_ENTRY_BIND_ADDRESS=] - --announce-ws-port - Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=] - --announce-wss-port - If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=] - --enforce-zk-nyms - Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible - values: true, false] - --mnemonic - Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be generated [env: NYMNODE_MNEMONIC=] - --upgrade-mode-attestation-url - Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTATION_URL=] - --upgrade-mode-attester-public-key - Expected public key of the entity signing the published attestation. This argument should never be set outside testnets and local networks [env: - NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=] - --upstream-exit-policy-url - Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] - --open-proxy - Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, - false] - --lp-control-bind-address - Bind address for the TCP LP control traffic. default: `[::]:41264` [env: NYMNODE_LP_CONTROL_BIND_ADDRESS=] - --lp-control-announce-port - Custom announced port for listening for the TCP LP control traffic. If unspecified, the value from the `lp_control_bind_address` will be used instead [env: - NYMNODE_LP_CONTROL_ANNOUNCE_PORT=] - --lp-data-bind-address - Bind address for the UDP LP data traffic. default: `[::]:51264` [env: NYMNODE_LP_DATA_BIND_ADDRESS=] - --lp-data-announce-port - Custom announced port for listening for the UDP LP data traffic. If unspecified, the value from the `lp_data_bind_address` will be used instead [env: NYMNODE_LP_DATA_ANNOUNCE_PORT=] - --lp-use-mock-ecash - Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, the LP listener will accept any credential without - blockchain verification [env: NYMNODE_LP_USE_MOCK_ECASH=] [possible values: true, false] - -h, --help - Print help + --id Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node] + --config-file Path to a configuration file of this node [env: NYMNODE_CONFIG=] + --accept-operator-terms-and-conditions Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at + [env: NYMNODE_ACCEPT_OPERATOR_TERMS=] + --deny-init Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: + NYMNODE_DENY_INIT=] + --init-only If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses + [env: NYMNODE_INIT_ONLY=] + --local Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=] + --mode [...] Specifies the current mode(s) of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, + exit-gateway, exit-providers-only] + --modes Specifies the current mode(s) of this nym-node as a single flag [env: NYMNODE_MODES=] [possible values: mixnode, + entry-gateway, exit-gateway, exit-providers-only] + -w, --write-changes If this node has been initialised before, specify whether to write any new changes to the config file [env: + NYMNODE_WRITE_CONFIG_CHANGES=] + --bonding-information-output Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding + information is still a subject to change and this argument should be treated only as a preview of future features [env: + NYMNODE_BONDING_INFORMATION_OUTPUT=] + -o, --output Specify the output format of the bonding information (`text` or `json`) [env: NYMNODE_OUTPUT=] [default: text] [possible + values: text, json] + --public-ips Comma separated list of public ip addresses that will be announced to the nym-api and subsequently to the clients. In + nearly all circumstances, it's going to be identical to the address you're going to use for bonding [env: + NYMNODE_PUBLIC_IPS=] + --hostname Optional hostname associated with this gateway that will be announced to the nym-api and subsequently to the clients [env: + NYMNODE_HOSTNAME=] + --location Optional **physical** location of this node's server. Either full country name (e.g. 'Poland'), two-letter alpha2 (e.g. + 'PL'), three-letter alpha3 (e.g. 'POL') or three-digit numeric-3 (e.g. '616') can be provided [env: NYMNODE_LOCATION=] + --http-bind-address Socket address this node will use for binding its http API. default: `[::]:8080` [env: NYMNODE_HTTP_BIND_ADDRESS=] + --landing-page-assets-path Path to assets directory of custom landing page of this node [env: NYMNODE_HTTP_LANDING_ASSETS=] + --http-access-token An optional bearer token for accessing certain http endpoints. Currently only used for prometheus metrics [env: + NYMNODE_HTTP_ACCESS_TOKEN=] + --expose-system-info Specify whether basic system information should be exposed. default: true [env: NYMNODE_HTTP_EXPOSE_SYSTEM_INFO=] + [possible values: true, false] + --expose-system-hardware Specify whether basic system hardware information should be exposed. default: true [env: + NYMNODE_HTTP_EXPOSE_SYSTEM_HARDWARE=] [possible values: true, false] + --expose-crypto-hardware Specify whether detailed system crypto hardware information should be exposed. default: true [env: + NYMNODE_HTTP_EXPOSE_CRYPTO_HARDWARE=] [possible values: true, false] + --mixnet-bind-address Address this node will bind to for listening for mixnet packets default: `[::]:1789` [env: NYMNODE_MIXNET_BIND_ADDRESS=] + --mixnet-announce-port If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node + is behind a proxy [env: NYMNODE_MIXNET_ANNOUNCE_PORT=] + --nym-api-urls Addresses to nym APIs from which the node gets the view of the network [env: NYMNODE_NYM_APIS=] + --nyxd-urls Addresses to nyxd chain endpoint which the node will use for chain interactions [env: NYMNODE_NYXD=] + --enable-console-logging Specify whether running statistics of this node should be logged to the console [env: NYMNODE_ENABLE_CONSOLE_LOGGING=] + [possible values: true, false] + --wireguard-enabled Specifies whether the wireguard service is enabled on this node [env: NYMNODE_WG_ENABLED=] [possible values: true, false] + --wireguard-bind-address Socket address this node will use for binding its wireguard interface. default: `[::]:51822` [env: + NYMNODE_WG_BIND_ADDRESS=] + --wireguard-tunnel-announced-port Tunnel port announced to external clients wishing to connect to the wireguard interface. Useful in the instances where the + node is behind a proxy [env: NYMNODE_WG_ANNOUNCED_PORT=] + --wireguard-private-network-prefix The prefix denoting the maximum number of the clients that can be connected via Wireguard. The maximum value for IPv4 is + 32 and for IPv6 is 128 [env: NYMNODE_WG_PRIVATE_NETWORK_PREFIX=] + --wireguard-userspace Use userspace implementation of WireGuard (wireguard-go) instead of kernel module. Useful in containerized environments + without kernel WireGuard support [env: NYMNODE_WG_USERSPACE=] [possible values: true, false] + --verloc-bind-address Socket address this node will use for binding its verloc API. default: `[::]:1790` [env: NYMNODE_VERLOC_BIND_ADDRESS=] + --verloc-announce-port If applicable, custom port announced in the self-described API that other clients and nodes will use. Useful when the node + is behind a proxy [env: NYMNODE_VERLOC_ANNOUNCE_PORT=] + --entry-bind-address Socket address this node will use for binding its client websocket API. default: `[::]:9000` [env: + NYMNODE_ENTRY_BIND_ADDRESS=] + --announce-ws-port Custom announced port for listening for websocket client traffic. If unspecified, the value from the `bind_address` will + be used instead [env: NYMNODE_ENTRY_ANNOUNCE_WS_PORT=] + --announce-wss-port If applicable, announced port for listening for secure websocket client traffic [env: NYMNODE_ENTRY_ANNOUNCE_WSS_PORT=] + --enforce-zk-nyms Indicates whether this gateway is accepting only coconut credentials for accessing the mixnet or if it also accepts + non-paying clients [env: NYMNODE_ENFORCE_ZK_NYMS=] [possible values: true, false] + --mnemonic Custom cosmos wallet mnemonic used for zk-nym redemption. If no value is provided, a fresh mnemonic is going to be + generated [env: NYMNODE_MNEMONIC=] + --upgrade-mode-attestation-url Endpoint to query to retrieve current upgrade mode attestation. This argument should never be set outside testnets and + local networks [env: NYMNODE_UPGRADE_MODE_ATTESTATION_URL=] + --upgrade-mode-attester-public-key Expected public key of the entity signing the published attestation. This argument should never be set outside testnets + and local networks [env: NYMNODE_UPGRADE_MODE_ATTESTER_PUBKEY=] + --upstream-exit-policy-url Specifies the url for an upstream source of the exit policy used by this node [env: NYMNODE_UPSTREAM_EXIT_POLICY=] + --open-proxy Specifies whether this exit node should run in 'open-proxy' mode and thus would attempt to resolve **ANY** request it + receives [env: NYMNODE_OPEN_PROXY=] [possible values: true, false] + --lp-control-bind-address Bind address for the TCP LP control traffic. default: `[::]:41264` [env: NYMNODE_LP_CONTROL_BIND_ADDRESS=] + --lp-control-announce-port Custom announced port for listening for the TCP LP control traffic. If unspecified, the value from the + `lp_control_bind_address` will be used instead [env: NYMNODE_LP_CONTROL_ANNOUNCE_PORT=] + --lp-data-bind-address Bind address for the UDP LP data traffic. default: `[::]:51264` [env: NYMNODE_LP_DATA_BIND_ADDRESS=] + --lp-data-announce-port Custom announced port for listening for the UDP LP data traffic. If unspecified, the value from the `lp_data_bind_address` + will be used instead [env: NYMNODE_LP_DATA_ANNOUNCE_PORT=] + --lp-use-mock-ecash Use mock ecash manager for LP testing. WARNING: Only use this for local testing! Never enable in production. When enabled, + the LP listener will accept any credential without blockchain verification [env: NYMNODE_LP_USE_MOCK_ECASH=] [possible + values: true, false] + -h, --help Print help ``` diff --git a/documentation/docs/pages/_app.tsx b/documentation/docs/pages/_app.tsx index f76d9a47ef..15e08f8a0a 100644 --- a/documentation/docs/pages/_app.tsx +++ b/documentation/docs/pages/_app.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { useMemo, useEffect } from 'react'; import type { AppProps } from 'next/app'; import './styles.css'; import { ThemeProvider, createTheme } from '@mui/material/styles'; @@ -20,6 +20,17 @@ const MyApp: React.FC = ({ Component, pageProps }) => { }), [], ); + useEffect(() => { + const handler = (e: MouseEvent) => { + const img = e.target as HTMLElement; + if (img.tagName === 'IMG' && img.closest('.nextra-content')) { + img.classList.toggle('img-expanded'); + } + }; + document.addEventListener('click', handler); + return () => document.removeEventListener('click', handler); + }, []); + const AnyComponent = Component as any; return ( diff --git a/documentation/docs/pages/network/architecture.mdx b/documentation/docs/pages/network/architecture.mdx index 19560c0bb2..a3b8086b9b 100644 --- a/documentation/docs/pages/network/architecture.mdx +++ b/documentation/docs/pages/network/architecture.mdx @@ -3,6 +3,4 @@ Core components: * A **Mixnet**, which mixes Sphinx packet traffic so that it cannot be determined who is communicating with whom. Our mixnet is based on a modified version of the [**Loopix** design](concepts/loopix). This is made up of [Nym nodes](architecture/mixnet#nodes) runnning on servers around the world maintained by a decentralised group of Operators. * Various [**Nym clients**](architecture/mixnet#nym-clients) which manage sending and receiving Sphinx packets, encrypting/decrypting traffic, and providing [cover traffic](./concepts/cover-traffic) to hide 'real' traffic timing. -* A CosmWasm-enabled blockchain called [**Nyx**](architecture/nyx), the home of the various smart contracts used by the mixnet. A subset of Nyx Validators run [NymAPI](./architecture/nyx#nymapi) instances, taking part in also producing and verifying [zk-nym credentials](cryptography/zk-nym). - -![arch_overview](/images/network/arch/overall-arch.png) +* A CosmWasm-enabled blockchain called [**Nyx**](architecture/nyx), the home of the various smart contracts used by the mixnet. A subset of Nyx Validators run [NymAPI](./architecture/nyx#nymapi) instances, taking part in also producing and verifying [zk-nym credentials](cryptography/zk-nym). \ No newline at end of file diff --git a/documentation/docs/pages/styles.css b/documentation/docs/pages/styles.css index 0e5c829a30..1de040d5d1 100644 --- a/documentation/docs/pages/styles.css +++ b/documentation/docs/pages/styles.css @@ -1,21 +1,67 @@ -/* nym.com-aligned colour tokens */ -:root { - --colorPrimary: #85E89D; - --textPrimary: #FFFFFF; - --bg-dark: #1E2426; - --border-dark: #2E3538; +/* nym docs – angular, clean styling (oxide-inspired) */ + +@font-face { + font-family: 'JetBrains Mono'; + src: url('/docs/fonts/JetBrainsMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; } -/* dark mode background override */ +@font-face { + font-family: 'JetBrains Mono'; + src: url('/docs/fonts/JetBrainsMono-Medium.woff2') format('woff2'); + font-weight: 500; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'JetBrains Mono'; + src: url('/docs/fonts/JetBrainsMono-SemiBold.woff2') format('woff2'); + font-weight: 600; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'JetBrains Mono'; + src: url('/docs/fonts/JetBrainsMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +:root { + --colorPrimary: #85E89D; + --textPrimary: #E0E0E0; + --textMuted: #8B9295; + --bg-dark: #181C1E; + --bg-code: #1C2022; + --border-dark: #2A2F32; + --border: #2A2F32; + --font-mono: 'JetBrains Mono', 'SFMono-Regular', 'Consolas', 'Liberation Mono', 'Menlo', monospace; +} + +/* ── Global: kill all rounded corners ── */ + +*, +*::before, +*::after { + border-radius: 0 !important; +} + +/* ── Dark mode backgrounds ── */ + html.dark { background-color: var(--bg-dark); } html.dark body { background-color: var(--bg-dark); + color: var(--textPrimary); } -/* nextra main content area bg */ html.dark .nextra-nav-container, html.dark .nextra-sidebar-container, html.dark .nextra-content, @@ -24,28 +70,43 @@ html.dark .dark\:nx-bg-dark { background-color: var(--bg-dark) !important; } +/* ── Typography ── */ + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-mono); + letter-spacing: -0.02em; + font-weight: 600; +} + +html.dark h1 { + color: #FFFFFF; + font-weight: 700; +} + +html.dark h2 { + color: #F0F0F0; + padding-bottom: 0.3em; + border-bottom: 1px solid var(--border-dark); +} + +html.dark p, html.dark li { + color: var(--textPrimary); + line-height: 1.7; +} + +/* ── Layout ── */ + footer { text-align: center; } .nextra-code-block > pre { - /* max-height: 350px !important; */ max-height: none !important; scroll-y: auto !important; } -/* set width entire div */ .nx-mx-auto.nx-flex.nx-max-w-\[90rem\] { max-width: 110rem; - left-padding: 1%; -} - -.nextra-toc { - width: 10px !important; - padding-right: 0px; - padding-left: 0px; - /* text-align: right; */ - border-left: 1px solid var(--border-dark); } .nextra-content { @@ -56,23 +117,26 @@ footer { margin: 0 !important; } -:is(html[class~="dark"] .dark\:nx-bg-primary-300\/10) { - background-color: hsl(var(black) 100% 77%/0.1) !important; +/* ── Nav bar ── */ + +html.dark .nextra-nav-container { + border-bottom: 1px solid var(--border-dark) !important; } -/* Sidebar active item */ -:is(html .dark\:nx-bg-primary-400\/10) { - background: transparent !important; - border-left: 2px solid #85E89D; - color: #FFFFFF !important; +html.dark .nextra-nav-container nav a, +html.dark .nextra-nav-container nav button, +html.dark .nextra-nav-container nav ul a { + font-family: var(--font-mono); + font-size: 0.85rem; } -:is(html:not(.dark) .dark\:nx-bg-primary-400\/10) { - background: transparent !important; - border-left: 2px solid #4A9E5C; - color: #242B2D !important; +html.dark .nextra-nav-container nav a:hover, +html.dark .nextra-nav-container nav ul a:hover { + color: var(--colorPrimary) !important; } +/* ── Sidebar ── */ + .nextra-sidebar-container { border-right: 1px solid var(--border-dark); width: 300px !important; @@ -82,21 +146,171 @@ footer { column-width: 500px; } -/* Links*/ +/* Sidebar font */ +html.dark .nextra-sidebar-container { + font-family: var(--font-mono); + font-size: 0.82rem; +} + +/* Sidebar text: muted by default */ +html.dark .nextra-sidebar-container a { + color: var(--textMuted) !important; +} + +html.dark .nextra-sidebar-container a:hover { + color: var(--textPrimary) !important; +} + +/* Sidebar separator labels (not active items) */ +html.dark .nextra-sidebar-container li.nx-mt-5 .nx-font-semibold { + color: var(--textMuted) !important; + font-size: 0.7rem; + letter-spacing: 0.05em; +} + +/* Active sidebar item: left-border accent + subtle tint */ +:is(html .dark\:nx-bg-primary-400\/10) { + background: rgba(133, 232, 157, 0.06) !important; + border-left: 2px solid var(--colorPrimary); + color: #FFFFFF !important; +} + +html.dark .nextra-sidebar-container :is(.dark\:nx-bg-primary-400\/10) a { + color: #FFFFFF !important; +} + +:is(html:not(.dark) .dark\:nx-bg-primary-400\/10) { + background: rgba(74, 158, 92, 0.08) !important; + border-left: 2px solid #4A9E5C; + color: #242B2D !important; +} + +:is(html[class~="dark"] .dark\:nx-bg-primary-300\/10) { + background-color: transparent !important; +} + +/* ── TOC (hidden — subsections live in sidebar) ── */ + +.nextra-toc { + display: none !important; +} + +/* ── Code blocks ── */ + +html.dark .nextra-code-block > pre { + background-color: var(--bg-code) !important; + border: 1px solid var(--border-dark); +} + +/* Language tag in code blocks */ +html.dark .nextra-code-block [class*="nx-absolute"] { + color: var(--textMuted); + font-size: 0.7rem; + text-transform: uppercase; + letter-spacing: 0.05em; +} + +/* Inline code: subtle background, no border */ +html.dark code:not(pre code) { + background-color: rgba(255, 255, 255, 0.06) !important; + border: none !important; + padding: 0.15em 0.4em; + font-size: 0.88em; + color: var(--textPrimary); +} + +/* ── Callouts: left-border accent ── */ + +html.dark .nextra-callout { + border: none; + border-left: 3px solid; + background: rgba(255, 255, 255, 0.02); +} + +/* ── Tables: monospace uppercase headers ── */ + +html.dark table { + border-collapse: collapse; + width: 100%; +} + +html.dark th { + border-bottom: 1px solid var(--border-dark); + text-align: left; + font-weight: 600; + font-family: var(--font-mono); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--textMuted); + padding: 0.6em 1em; +} + +html.dark td { + border-bottom: 1px solid var(--border-dark); + padding: 0.6em 1em; + color: var(--textPrimary); +} + +html.dark tr:hover td { + background-color: rgba(255, 255, 255, 0.02); +} + +/* ── Search box ── */ + +html.dark .nextra-search input { + border: 1px solid var(--border-dark); + background-color: var(--bg-dark) !important; + color: var(--textMuted); + font-family: var(--font-mono); + font-size: 0.8rem; +} + +html.dark .nextra-search input::placeholder { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.72rem; +} + +/* ── Links ── */ + .nx-text-primary-600.nx-underline.nx-decoration-from-font { color: var(--colorPrimary) !important; } -.MuiTypography-root.MuiTypography-inherit.MuiLink-root.MuiLink-underlineAlways.css-1xr3c94-MuiTypography-root-MuiLink-root { +.nx-text-primary-600 { + color: var(--colorPrimary) !important; +} + +.nextra-scrollbar.nx-sticky { + color: var(--colorPrimary) !important; +} + +/* ── Breadcrumbs / navigation links ── */ + +html.dark .nextra-breadcrumb { + color: var(--textMuted); +} + +/* Prev/next page links */ +html.dark a.nx-flex.nx-items-center { + border-color: var(--border-dark) !important; +} + +html.dark a.nx-flex.nx-items-center:hover { + border-color: var(--colorPrimary) !important; +} + +/* ── Material UI overrides ── */ + +.MuiTypography-root.MuiTypography-inherit.MuiLink-root.MuiLink-underlineAlways { color: var(--colorPrimary) !important; } -/* Chips*/ .chipContained { background-color: var(--colorPrimary) !important; } -/* Buttons */ .MuiButton-root { color: var(--colorPrimary) !important; border-color: var(--colorPrimary) !important; @@ -111,11 +325,7 @@ footer { color: var(--colorPrimary) !important; } -.nextra-scrollbar.nx-sticky { - color: var(--colorPrimary) !important; -} - -.nx-text-primary-600 { +a.MuiLink-root { color: var(--colorPrimary) !important; } @@ -127,10 +337,167 @@ input:focus-visible { border-color: var(--colorPrimary) !important; } -a.MuiLink-root { - color: var(--colorPrimary) !important; +.MuiPaper-root.MuiAccordion-root { + border-radius: 0; } -.MuiPaper-root.MuiAccordion-root { - border-radius: 8px; +/* ── Landing page ── */ + +.landing-heading { + color: #FFFFFF; +} + +.landing-card:hover { + background-color: rgba(255, 255, 255, 0.03) !important; +} + +/* ── Invert diagrams in dark mode ── */ + +html.dark .nextra-content img:not([src*="landing"]) { + filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(1.1); + mix-blend-mode: lighten; + cursor: zoom-in; +} + +/* Expanded image overlay */ +.nextra-content img.img-expanded { + position: fixed !important; + top: 0; + left: 0; + width: 100vw !important; + height: 100vh !important; + max-width: none !important; + object-fit: contain; + background: rgba(0, 0, 0, 0.9); + z-index: 9999; + cursor: zoom-out; + padding: 2rem; + mix-blend-mode: normal; + filter: none; +} + +html.dark .nextra-content img.img-expanded { + filter: invert(1) hue-rotate(180deg) brightness(0.92) contrast(1.1); + mix-blend-mode: normal; +} + +/* ── Light mode ── */ + +html:not(.dark) { + --colorPrimary: #1a7a32; + --colorPrimary-light: #1a7a32; + --bg-light: #F5F5F5; + --border-light: #E0E0E0; + --border: #E0E0E0; + --textMuted: #6B7280; + --textMuted-light: #6B7280; +} + +html:not(.dark) body { + background-color: var(--bg-light); +} + +html:not(.dark) .nextra-nav-container, +html:not(.dark) .nextra-sidebar-container, +html:not(.dark) .nextra-content, +html:not(.dark) .nx-bg-white { + background-color: var(--bg-light) !important; +} + +/* Light mode links: darker green */ +html:not(.dark) .nx-text-primary-600 { + color: var(--colorPrimary-light) !important; +} + +html:not(.dark) .nx-text-primary-600.nx-underline.nx-decoration-from-font { + color: var(--colorPrimary-light) !important; +} + +/* Light mode sidebar */ +html:not(.dark) .nextra-sidebar-container { + font-family: var(--font-mono); + font-size: 0.82rem; + border-right: 1px solid var(--border-light); +} + +html:not(.dark) .nextra-sidebar-container a { + color: var(--textMuted-light) !important; +} + +html:not(.dark) .nextra-sidebar-container a:hover { + color: #111 !important; +} + +/* Light mode nav */ +html:not(.dark) .nextra-nav-container { + border-bottom: 1px solid var(--border-light) !important; +} + +html:not(.dark) .nextra-nav-container nav a, +html:not(.dark) .nextra-nav-container nav button, +html:not(.dark) .nextra-nav-container nav ul a { + font-family: var(--font-mono); + font-size: 0.85rem; +} + +html:not(.dark) .nextra-nav-container nav a:hover, +html:not(.dark) .nextra-nav-container nav ul a:hover { + color: var(--colorPrimary-light) !important; +} + +/* Light mode code */ +html:not(.dark) code:not(pre code) { + background-color: rgba(0, 0, 0, 0.05) !important; + border: none !important; + padding: 0.15em 0.4em; + font-size: 0.88em; +} + +/* Light mode callouts */ +html:not(.dark) .nextra-callout { + border: none; + border-left: 3px solid; + background: rgba(0, 0, 0, 0.02); +} + +/* Light mode tables */ +html:not(.dark) th { + font-family: var(--font-mono); + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--textMuted-light); +} + +/* Light mode search */ +html:not(.dark) .nextra-search input { + font-family: var(--font-mono); + font-size: 0.8rem; + border: 1px solid var(--border-light); + background-color: #FFFFFF !important; +} + +html:not(.dark) .nextra-search input::placeholder { + text-transform: uppercase; + letter-spacing: 0.08em; + font-size: 0.72rem; +} + +/* Light mode h2 border */ +html:not(.dark) h2 { + padding-bottom: 0.3em; + border-bottom: 1px solid var(--border-light); +} + +/* Light mode landing page */ +html:not(.dark) .landing-heading { + color: #1a1a1a !important; +} + +html:not(.dark) .landing-card:hover { + background-color: rgba(0, 0, 0, 0.02) !important; +} + +html:not(.dark) .landing-card { + border-color: var(--border-light) !important; } diff --git a/documentation/docs/public/fonts/JetBrainsMono-Bold.woff2 b/documentation/docs/public/fonts/JetBrainsMono-Bold.woff2 new file mode 100644 index 0000000000..4917f43410 Binary files /dev/null and b/documentation/docs/public/fonts/JetBrainsMono-Bold.woff2 differ diff --git a/documentation/docs/public/fonts/JetBrainsMono-LICENSE.txt b/documentation/docs/public/fonts/JetBrainsMono-LICENSE.txt new file mode 100644 index 0000000000..8bee4148c1 --- /dev/null +++ b/documentation/docs/public/fonts/JetBrainsMono-LICENSE.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/documentation/docs/public/fonts/JetBrainsMono-Medium.woff2 b/documentation/docs/public/fonts/JetBrainsMono-Medium.woff2 new file mode 100644 index 0000000000..669d04cdf2 Binary files /dev/null and b/documentation/docs/public/fonts/JetBrainsMono-Medium.woff2 differ diff --git a/documentation/docs/public/fonts/JetBrainsMono-Regular.woff2 b/documentation/docs/public/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 0000000000..40da427651 Binary files /dev/null and b/documentation/docs/public/fonts/JetBrainsMono-Regular.woff2 differ diff --git a/documentation/docs/public/fonts/JetBrainsMono-SemiBold.woff2 b/documentation/docs/public/fonts/JetBrainsMono-SemiBold.woff2 new file mode 100644 index 0000000000..5ead7b0d6f Binary files /dev/null and b/documentation/docs/public/fonts/JetBrainsMono-SemiBold.woff2 differ diff --git a/documentation/docs/theme.config.tsx b/documentation/docs/theme.config.tsx index 8906294882..c223e68178 100644 --- a/documentation/docs/theme.config.tsx +++ b/documentation/docs/theme.config.tsx @@ -139,7 +139,7 @@ const config: DocsThemeConfig = { ); }, - logo: Nym Docs, + logo: Nym Docs, project: { link: "https://github.com/nymtech/nym", }, diff --git a/lychee.toml b/lychee.toml index 42ea21812d..7dff1a851a 100644 --- a/lychee.toml +++ b/lychee.toml @@ -9,7 +9,7 @@ fallback_extensions = ["mdx", "md"] # Remap /images to public/images for NextJS static assets # Lychee resolves /images to pages/images, but Next.js serves from public/images -remap = ["pages/images public/images"] +remap = ["pages/images public/images", "pages/docs/fonts public/fonts"] # Exclude component snippets (TODO: verify which are still in use) exclude_path = ["components/"]