From f195fd6a37241fae49957ff5f713e94f9065b67f Mon Sep 17 00:00:00 2001 From: Maya Date: Mon, 20 Jul 2026 14:13:03 -0400 Subject: [PATCH 1/5] Add inbound internal links to orphaned guides Three live guides (ISO 27001, HIPAA, Podman) had no inbound internal links and were reachable only from the /guides index. Cross-link the compliance series, add a reciprocal Docker->Podman link, and route auditor-driven readers from the network-documentation-software hub to the three control guides. --- src/lib/guides/network-documentation-hipaa.md | 2 +- src/lib/guides/network-documentation-iso27001.md | 2 +- src/lib/guides/network-documentation-nis2.md | 2 +- src/lib/guides/network-documentation-software.md | 1 + src/lib/guides/visualize-docker-containers-network.md | 2 ++ 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/guides/network-documentation-hipaa.md b/src/lib/guides/network-documentation-hipaa.md index ffcab95..b8f41ea 100644 --- a/src/lib/guides/network-documentation-hipaa.md +++ b/src/lib/guides/network-documentation-hipaa.md @@ -98,4 +98,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views (physical, logical, workloads, applications) that updates on a schedule and exports for audit use. For a HIPAA covered entity, its job is to keep the systems-and-data-flow map your risk analysis depends on accurate without anyone maintaining it by hand. It runs alongside your monitoring, access control, and risk-management work, not in place of them. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. If you want the deeper category overview, see the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Covered entities that also certify to a security standard or operate in the EU will find the same map doing double duty: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for NIS2](/guides/network-documentation-nis2) cover those controls. If you want the deeper category overview, see the guide to [network documentation software](/guides/network-documentation-software). diff --git a/src/lib/guides/network-documentation-iso27001.md b/src/lib/guides/network-documentation-iso27001.md index 947e0e8..d353dad 100644 --- a/src/lib/guides/network-documentation-iso27001.md +++ b/src/lib/guides/network-documentation-iso27001.md @@ -97,4 +97,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views (physical, logical, workloads, applications) that updates on a schedule and exports for evidence. For an organization certifying to ISO 27001, its job is to keep the network diagram and asset inventory that A.8.20 and A.5.9 assume you maintain accurate on their own, so producing current documentation for the auditor stops being manual work. It runs alongside the rest of your ISMS, not in place of it. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support, and [pricing](/pricing) covers the plan tiers. For a neighboring obligation, see the guide to [network documentation for NIS2](/guides/network-documentation-nis2), and for the broader category, the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support, and [pricing](/pricing) covers the plan tiers. For neighboring obligations, see the guides to [network documentation for NIS2](/guides/network-documentation-nis2) and [network documentation for HIPAA](/guides/network-documentation-hipaa), and for the broader category, the guide to [network documentation software](/guides/network-documentation-software). diff --git a/src/lib/guides/network-documentation-nis2.md b/src/lib/guides/network-documentation-nis2.md index 8cf4aa8..52a0b62 100644 --- a/src/lib/guides/network-documentation-nis2.md +++ b/src/lib/guides/network-documentation-nis2.md @@ -91,4 +91,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views that updates on a schedule and exports for evidence. For an entity under NIS2, its job is to keep the asset inventory and network map that the risk-analysis, asset-management, and continuity measures assume you maintain accurate on their own, so producing current documentation stops being manual work. It runs alongside the rest of your risk-management program, not in place of it. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. For the broader category, see the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Entities that fall under more than one regime map much of the same evidence twice: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for HIPAA](/guides/network-documentation-hipaa) cover those controls. For the broader category, see the guide to [network documentation software](/guides/network-documentation-software). diff --git a/src/lib/guides/network-documentation-software.md b/src/lib/guides/network-documentation-software.md index 316322d..f1809d5 100644 --- a/src/lib/guides/network-documentation-software.md +++ b/src/lib/guides/network-documentation-software.md @@ -78,6 +78,7 @@ That's why Scanopy prices [flat regardless of host count](/pricing). Pricing doc - **You already have monitoring and just need current documentation:** a dedicated documentation tool that runs alongside it, priced independently. - **You need a structured source of truth for automation to consume:** NetBox (its data model feeds Ansible, Nornir, Terraform), with its own NetBox Discovery agent or another feeder keeping it current. Pair it with a visualization tool, since NetBox has no native maps. - **You only need a one-time diagram for a presentation:** a manual tool like draw.io. Don't buy documentation software for a single drawing. +- **An auditor or regulator is the reason you are looking:** start from the control you have to satisfy, since that determines what the documentation has to show. Separate guides map the requirements for [NIS2](/guides/network-documentation-nis2), [ISO 27001](/guides/network-documentation-iso27001), and [HIPAA](/guides/network-documentation-hipaa) to the evidence each one expects. ## Scanopy documents the network and runs alongside monitoring and ITAM diff --git a/src/lib/guides/visualize-docker-containers-network.md b/src/lib/guides/visualize-docker-containers-network.md index 22201b2..16541c5 100644 --- a/src/lib/guides/visualize-docker-containers-network.md +++ b/src/lib/guides/visualize-docker-containers-network.md @@ -107,3 +107,5 @@ Either way, detection picks up containers wherever they run, so a mixed homelab Scanopy is not a container manager. It will not start, stop, or deploy anything, and it is not trying to replace Portainer, Komodo, or your orchestrator. Those tools manage containers. Scanopy shows you where your containers sit on the network and what they connect to. They work well side by side: manage in your container tool, see the topology in Scanopy. If you are mapping the rest of your gear too, the same scan that finds your containers also walks your switches and routers over SNMP. Here is the deeper version of that: [exploring your network topology with SNMP](/guides/snmp-network-topology-mapping). And Scanopy fingerprints [the services running on each host](/services), so a host is not just an IP, it is "the box running Postgres, Nginx, and twelve containers." + +Running Podman instead of, or alongside, Docker? Discovery works the same way, but pods change how ports and networks are reported. The guide to [visualizing Podman containers and pods](/guides/visualize-podman-containers-network) covers what is different. From 6b7f35a1ab49a403aa9badb6810999ed7201fad2 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 22 Jul 2026 12:16:57 -0400 Subject: [PATCH 2/5] SEO/content restructure + /compliance landing page Consolidate around the vocabulary that converts and stop chasing the 'network diagram tool' head term (#20 slid pos 7->18 on drawing-tool intent). - Deepen the SolarWinds NTM fixture entry (cited); singular framing on all alternatives-page intros - Strengthen the network-documentation-template blog (best non-brand asset) - Extract the shared content-segment parser; new /guides/open-source-network- documentation for the OSS/self-hosted cluster - Retarget #20 to 'network documentation tools' and merge the network- documentation-software guide into it (301, slug kept); repoint all inbound links + anchor text - New /compliance landing page (homepage/product patterns): audit-ready hero, NIS2/ISO27001/HIPAA cards, ViewSwitcher visual, EvidenceExports as cards (data-driven, shared with the guides' prose), Motala quote; outcome-led headers; compliance guides link up to it - Copy: no personification/virtue/false claims; edition framing correct (commercial = self-hosted with caps removed; Cloud = managed) --- .../blog/automated-network-documentation.md | 4 +- src/lib/blog/network-diagrams-wrong.md | 2 +- .../blog/network-documentation-template.md | 6 +- src/lib/blog/network-person-leaves.md | 2 +- src/lib/compare/alternatives-pages.ts | 5 + .../best-automated-network-diagram-tools.md | 40 ++- src/lib/components/EvidenceExports.svelte | 96 ++++-- src/lib/components/Footer.svelte | 3 + src/lib/components/VendorComparison.svelte | 2 +- src/lib/content/contentSegments.ts | 74 +++++ src/lib/fixtures/network-diagram-vendors.ts | 12 +- src/lib/guides/network-documentation-hipaa.md | 2 +- .../guides/network-documentation-iso27001.md | 2 +- src/lib/guides/network-documentation-nis2.md | 2 +- .../guides/network-documentation-software.md | 85 ----- src/lib/guides/network-mapping-software.md | 2 +- .../open-source-network-documentation.md | 52 ++++ .../guides/snmp-network-topology-mapping.md | 2 +- src/routes/+page.svelte | 11 +- src/routes/blog/[slug]/+page.server.ts | 56 +--- src/routes/blog/[slug]/+page.svelte | 7 +- .../[slug=alternatives]/+page.svelte | 4 +- src/routes/comparisons/[slug]/+page.server.ts | 2 +- src/routes/comparisons/[slug]/+page.svelte | 4 +- .../scanopy-alternatives/+page.svelte | 4 +- src/routes/comparisons/vs/[slug]/+page.svelte | 4 +- src/routes/compliance/+page.svelte | 294 ++++++++++++++++++ src/routes/guides/[slug]/+page.server.ts | 64 +--- src/routes/guides/[slug]/+page.svelte | 9 +- src/routes/services/+page.svelte | 2 +- src/routes/sitemap.xml/+server.ts | 1 + static/_redirects | 5 + 32 files changed, 578 insertions(+), 282 deletions(-) create mode 100644 src/lib/content/contentSegments.ts delete mode 100644 src/lib/guides/network-documentation-software.md create mode 100644 src/lib/guides/open-source-network-documentation.md create mode 100644 src/routes/compliance/+page.svelte diff --git a/src/lib/blog/automated-network-documentation.md b/src/lib/blog/automated-network-documentation.md index 65c3d62..2275ffc 100644 --- a/src/lib/blog/automated-network-documentation.md +++ b/src/lib/blog/automated-network-documentation.md @@ -128,13 +128,13 @@ Most teams need all three, and one tool does not cover the others. Monitoring to ## Tools That Automate Network Documentation -Several tools approach automated documentation from different angles. Some are dedicated documentation tools, others bundle mapping into a monitoring or asset management platform (for the full category overview, including open-source options, see our [network documentation software guide](/guides/network-documentation-software)): +Several tools approach automated documentation from different angles. Some are dedicated documentation tools, others bundle mapping into a monitoring or asset management platform (for the full comparison, including pricing, see the [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools), and for the free options, the [open-source network documentation guide](/guides/open-source-network-documentation)): ## What to Look for in an Automated Documentation Tool -If you're evaluating tools, our [comparison of automated network diagram tools](/comparisons/best-automated-network-diagram-tools) covers specific products, pricing, and trade-offs. Six criteria: +If you're evaluating tools, our [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools) covers specific products, pricing, and trade-offs. Six criteria: - **Lightweight deployment**: one scanner per network, not an agent on every device. You're solving a complexity problem; don't add more complexity - **Protocol support**: SNMP, LLDP, and CDP at minimum. ARP and MAC table scanning for full topology diff --git a/src/lib/blog/network-diagrams-wrong.md b/src/lib/blog/network-diagrams-wrong.md index 57433d5..3ed7d4d 100644 --- a/src/lib/blog/network-diagrams-wrong.md +++ b/src/lib/blog/network-diagrams-wrong.md @@ -72,7 +72,7 @@ What to look for: - **Intuitive visualization**: Not just a device list. An interactive map showing how things connect, that anyone can quickly get up to speed on. - **Shareable output**: The whole team should benefit without everyone needing a login or a license. -If you want to see how the options stack up on those criteria, we put together a [comparison of automated network diagram tools](/comparisons/best-automated-network-diagram-tools) covering discovery method, live updates, and list pricing. +If you want to see how the options stack up on those criteria, we put together a [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools) covering discovery method, live updates, and list pricing. We built [Scanopy](/) because we had this exact problem. Deploy a lightweight daemon on your network, point it at your subnets, and it discovers devices, maps connections via SNMP, and generates a topology map that updates itself on every scan. It detects [over 200 services](/services) automatically. New device shows up? It's on the next map. diff --git a/src/lib/blog/network-documentation-template.md b/src/lib/blog/network-documentation-template.md index 2617b7a..ffc1365 100644 --- a/src/lib/blog/network-documentation-template.md +++ b/src/lib/blog/network-documentation-template.md @@ -2,7 +2,7 @@ title: Network Documentation Template (Free) + Why Templates Fail description: Free network documentation template covering device inventory, IP addressing, and VLANs. Download it, then learn why templates always go stale. date: 2026-03-04 -dateModified: 2026-07-14 +dateModified: 2026-07-21 keyword: network documentation template slug: network-documentation-template tldr: A network documentation template covering device inventory, IP addressing, VLANs, and connections. Templates are a reasonable starting point, but they go out of date within days. Automated discovery keeps documentation accurate without the upkeep. @@ -51,6 +51,8 @@ Also, my homelab was simple. ### Topology Notes +This is the part a table can't hold: how the segments connect, what's deliberately isolated from what, and the one or two decisions future-you will have forgotten the reason for. Keep it to a few lines. It's the section that saves you at 3am. + - Internet -> router01 -> LAN (flat network) - WireGuard on beelink01 for remote access - Guest network isolated by router, internet only @@ -98,7 +100,7 @@ That is why I stopped maintaining a spreadsheet. And yes, you can embed your own I built this for my homelab. Brandon Lee at VirtualizationHowTo [put it through its paces](https://www.virtualizationhowto.com/2025/12/stop-drawing-network-diagrams-manually-scanopy-does-it-for-you/) on a production network and came to the same conclusion: manual diagrams don't survive contact with a real environment. -Scanopy isn't the only tool that does this. If you want to weigh the options, we [compared the best automated network diagram tools](/comparisons/best-automated-network-diagram-tools) by discovery method, pricing, and how often the map updates. +Scanopy isn't the only tool that does this. If you want to weigh the options, we [compared network documentation tools](/comparisons/best-automated-network-diagram-tools) by discovery method, pricing, and how often the map updates. The template gives you a snapshot of column headers. Scanopy gives you an interactive, shareable map you can embed anywhere, and it can't go stale because it's reading the network directly. If you still want the spreadsheet, Scanopy exports CSVs and has an API. Same data, always current. diff --git a/src/lib/blog/network-person-leaves.md b/src/lib/blog/network-person-leaves.md index 1f10405..a19d1e8 100644 --- a/src/lib/blog/network-person-leaves.md +++ b/src/lib/blog/network-person-leaves.md @@ -98,7 +98,7 @@ You don't need a month-long documentation initiative. Start with these: 2. **List the undocumented decisions.** Not the configs (those are in the devices) but the _why_ behind the configs. Why is the network segmented this way? Why does that rule exist? Write down the reasoning, not just the settings. -3. **Run an automated discovery scan.** Even a single scan gives the next person a starting point. They'll know what's on the network without having to trace cables. If you're picking a tool, our [comparison of automated network diagram tools](/comparisons/best-automated-network-diagram-tools) breaks down discovery method and pricing across the main options. +3. **Run an automated discovery scan.** Even a single scan gives the next person a starting point. They'll know what's on the network without having to trace cables. If you're picking a tool, our [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools) breaks down discovery method and pricing across the main options. 4. **Export your current network state.** If your network topology lives in someone's head, get it out. A rough diagram is better than no diagram. An [auto-generated one](/blog/network-diagrams-wrong) is better than both. diff --git a/src/lib/compare/alternatives-pages.ts b/src/lib/compare/alternatives-pages.ts index 5d5e679..e92685c 100644 --- a/src/lib/compare/alternatives-pages.ts +++ b/src/lib/compare/alternatives-pages.ts @@ -102,6 +102,11 @@ export function buildAltIntro(vendor: Vendor): string { const name = vendorDisplayName(vendor); const parts: string[] = []; + // Lead with the singular "alternative" framing so the page covers both the singular + // query ("librenms alternative") and the plural one ("librenms alternatives"); the + // title, H1, and H2 already carry the plural. + parts.push(`Looking for a ${name} alternative?`); + if (vendor.bestFor) { parts.push(`${name} is built for ${lowerFirst(trimSentence(vendor.bestFor))}.`); } diff --git a/src/lib/comparisons/best-automated-network-diagram-tools.md b/src/lib/comparisons/best-automated-network-diagram-tools.md index d8ccde9..9c18bfc 100644 --- a/src/lib/comparisons/best-automated-network-diagram-tools.md +++ b/src/lib/comparisons/best-automated-network-diagram-tools.md @@ -1,18 +1,18 @@ --- -title: Best Automated Network Diagram Tools 2026 (Real Pricing) -description: '13 automated network diagram tools compared on discovery method, live updates, and list pricing, including free and open-source options. Updated for 2026.' -keyword: best automated network diagram tool +title: 'Network Documentation Tools 2026: 13 Compared (Real Pricing)' +description: '13 network documentation tools compared on discovery method, live updates, and real pricing, including free and open-source options. Updated for 2026.' +keyword: network documentation tools slug: best-automated-network-diagram-tools date: 2026-04-01 -dateModified: 2026-07-14 +dateModified: 2026-07-21 style: comparison -tldr: "The best automated network diagram tool for most IT teams is one that combines auto-discovery with exportable, shareable diagrams, not a monitoring dashboard with a map tab bolted on. Most tools marketed as 'automated' are either monitoring platforms that include mapping as a feature, or manual drawing tools with no discovery. Here's what each of the 13 tools does, what it costs, and which one fits your network." +tldr: "The best network documentation tool for most IT teams combines automatic discovery with an exportable, shareable map, not a monitoring dashboard with a map tab bolted on. Most tools marketed as 'automated' are either monitoring platforms that include mapping as a feature, or manual drawing tools with no discovery. Here's what each of the 13 tools does, what it costs, and which one fits your network." ctaDescription: Scanopy deploys a lightweight daemon that discovers your network and builds a live topology map. No per-device fees, unlimited hosts. It pairs with whatever monitoring tool you already use. --- -## Three Categories: Monitoring Platforms, Dedicated Diagram Tools, and Manual Drawing Tools +## Network Documentation Tools Fall Into Three Categories: Monitoring Platforms, Dedicated Tools, and Manual Drawing Tools -There are three categories of network diagram tools: **monitoring platforms** that include mapping as a feature, **dedicated diagram tools** that focus only on documentation, and **manual diagramming tools** where you draw everything yourself. The right choice of network diagram software depends on whether you need monitoring bundled in, how often diagrams need to update, and your budget. If you think of this as building a live map rather than a static diagram, see our [network mapping software](/guides/network-mapping-software) guide. +There are three categories of network documentation tools: **monitoring platforms** that include mapping as a feature, **dedicated documentation tools** that focus only on documenting the network, and **manual diagramming tools** where you draw everything yourself. The right choice depends on whether you need monitoring bundled in, how often the documentation needs to update, and your budget. If you think of this as building a live map rather than a static diagram, see our [network mapping software](/guides/network-mapping-software) guide. | Scenario | Recommendation | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | @@ -24,9 +24,19 @@ There are three categories of network diagram tools: **monitoring platforms** th | Budget monitoring + mapping | [Domotz](#domotz) ($1.50/device/mo) or [ManageEngine](#manageengine-opmanager) ($95/yr) | | Free and self-hosted | [LibreNMS](#librenms) for monitoring with basic maps. [NetDisco](#netdisco) for Layer 2 topology discovery. [Scanopy Community Edition](/community) for documentation-focused mapping. [draw.io](#drawio) for manual diagrams. All free. | -## "Automated" Means the Tool Discovers the Network and Draws the Diagram for You +## A Network Documentation Tool Answers Three Questions and Keeps the Answers Current -"Automated" means the tool discovers your network and produces a diagram without you drawing anything. Most tools on this list do that. don't. They're manual diagramming tools, included because they are commonly recommended for this query. ([Here's a deeper look at how automated network documentation works](/blog/automated-network-documentation), and if you're weighing the broader category beyond diagrams (inventory, service detection, exports), see our [network documentation software guide](/guides/network-documentation-software).) +Good network documentation software keeps the answers to three questions accurate as the network changes: + +- **What is on the network?** Hosts, their IP and MAC addresses, hostnames, and vendors. The deeper tools also fingerprint the services running on each host, so a box isn't just an IP, it's "the machine running Postgres, Nginx, and a stack of containers." +- **How is it connected?** Physical links between switches, routers, and hosts, plus the logical layer of subnets and VLANs. This is the part manual diagrams get wrong first, because cabling and topology change without anyone updating the drawing. +- **What changed?** A record you can trust over time, not a file someone last touched eighteen months ago. + +The output is usually a topology map plus a searchable inventory. A documentation tool derives that data from discovery; a drawing tool derives it from memory. + +## "Automated" Means the Tool Discovers the Network and Documents It for You + +"Automated" means the tool discovers your network and produces the documentation without you drawing anything. Most tools on this list do that. don't. They're manual diagramming tools, included because they are commonly recommended for this query. ([Here's a deeper look at how automated network documentation works](/blog/automated-network-documentation), and for the free, self-hosted options specifically, the [open-source network documentation guide](/guides/open-source-network-documentation).)
SNMP (Simple Network Management Protocol)
@@ -45,6 +55,18 @@ There are three categories of network diagram tools: **monitoring platforms** th
Identifying what software or services (web servers, databases, DNS) are running on each host, beyond just detecting that the host exists. Nmap and Scanopy perform deep service fingerprinting; most other tools on this list do not.
+## Documentation, Monitoring, and ITAM Are Not the Same Thing + +This category gets muddy because three different kinds of tools all touch your device inventory. They are not substitutes, and several tools on this list are really monitoring platforms with a map feature. + +| Tool type | Core job | Priced for | +| -------------------------- | --------------------------------------------------------------------- | ---------------------------- | +| Network documentation | Keep an accurate map and inventory of what exists and how it connects | Completeness (whole network) | +| Network monitoring | Watch device health, alert on problems, track performance over time | Per-device attention | +| IT asset management (ITAM) | Track ownership, lifecycle, licensing, and procurement of assets | Asset records | + +Most teams need more than one of these. Documentation software tells you the network's shape; monitoring tells you when part of it is on fire; ITAM tells you who owns the laptop and when its warranty ends. The right setup runs them alongside each other, not one instead of another. + ## Per-Device Pricing Makes Complete Documentation the Most Expensive Option Per-device pricing fits monitoring because monitoring's value is per-device, and it can be tuned to match. You watch critical devices closely and can sample, throttle, or skip the rest, so paying in proportion to what you actively monitor lines up with the value you get (and with the cost, since continuous polling, storage, and alerting scale per device). diff --git a/src/lib/components/EvidenceExports.svelte b/src/lib/components/EvidenceExports.svelte index 318b63c..feff1d9 100644 --- a/src/lib/components/EvidenceExports.svelte +++ b/src/lib/components/EvidenceExports.svelte @@ -1,32 +1,70 @@ -

Scanopy gets the current map out three ways:

- -

- Those show the network as it is now. Auditors and authorities also ask whether it is kept current - over time, and topology snapshots are the feature for that. A snapshot is a saved, dated capture of - the network state; take one at each audit or review and you have a record of what the network looked - like on that date and what changed since. That dated record is the evidence your documentation is - maintained, not reconstructed for the assessment. -

+{#if layout === 'cards'} +
+ {#each methods as m (m.title)} +
+ {m.title} + {m.what} + {m.auditorUse} +
+ {/each} +
+{:else} +

Scanopy gets the current map out three ways:

+ + {#if snapshot} +

+ Those show the network as it is now. Auditors and authorities also ask whether it is kept + current over time. {snapshot.title}: + {snapshot.what} + {snapshot.auditorUse} +

+ {/if} +{/if} diff --git a/src/lib/components/Footer.svelte b/src/lib/components/Footer.svelte index f5774b7..6a9ce43 100644 --- a/src/lib/components/Footer.svelte +++ b/src/lib/components/Footer.svelte @@ -172,6 +172,9 @@
  • Comparisons
  • +
  • + Compliance +
  • diff --git a/src/lib/components/VendorComparison.svelte b/src/lib/components/VendorComparison.svelte index d02b121..a472149 100644 --- a/src/lib/components/VendorComparison.svelte +++ b/src/lib/components/VendorComparison.svelte @@ -231,7 +231,7 @@

    For a detailed comparison of these and other tools, see our full comparison of automated network diagram toolsfull network documentation tools comparison.

    {/if} diff --git a/src/lib/content/contentSegments.ts b/src/lib/content/contentSegments.ts new file mode 100644 index 0000000..5b37b82 --- /dev/null +++ b/src/lib/content/contentSegments.ts @@ -0,0 +1,74 @@ +// Shared inline-component parser for markdown content routes (blog, guides, and any +// future route that renders authored markdown). Splits rendered HTML on inline markers +// so they render as Svelte components instead of raw HTML, letting a chosen vendor subset +// table, a live demo embed, a customer quote, the shared exports block, or a captioned +// topology figure appear inline in prose on WHATEVER route the content belongs on — the +// marker set is defined once here, not copied per route. +// +// Markers: +// arbitrary vendor subset table +// theme-aware live map embed +// customer quote + logo +// shared exports/embed/share block +// captioned topology screenshot + +export type ContentSegment = + | { type: 'html'; content: string } + | { type: 'vendor-inline-table'; vendorSlugs: string[]; columns: string[] } + | { type: 'scanopy-demo' } + | { type: 'customer-quote'; id: string } + | { type: 'evidence-exports' } + | { type: 'topology-figure'; view: string }; + +export const DEFAULT_INLINE_COLUMNS = ['name', 'discovery', 'pricing', 'bestFor']; + +/** + * Splits rendered markdown HTML into content segments on the inline-component markers above. + * Returns null when the content has none of the markers (caller renders the raw HTML instead). + * The second return value is the de-duplicated set of vendor slugs referenced by any + * vendor-table markers, so the caller can hydrate just the vendor data it needs. + */ +export function splitContentSegments( + html: string +): { segments: ContentSegment[]; vendorSlugs: string[] } | null { + const markerRegex = + //g; + if (!markerRegex.test(html)) return null; + + const segments: ContentSegment[] = []; + const allSlugs: string[] = []; + let lastIndex = 0; + markerRegex.lastIndex = 0; + let match: RegExpExecArray | null; + + while ((match = markerRegex.exec(html)) !== null) { + const before = html.slice(lastIndex, match.index).trim(); + if (before) { + segments.push({ type: 'html', content: before }); + } + + if (match[1]) { + const vendorSlugs = match[1].split(',').filter(Boolean); + const columns = match[2] ? match[2].split(',').filter(Boolean) : DEFAULT_INLINE_COLUMNS; + allSlugs.push(...vendorSlugs); + segments.push({ type: 'vendor-inline-table', vendorSlugs, columns }); + } else if (match[3]) { + segments.push({ type: 'customer-quote', id: match[3] }); + } else if (match[4]) { + segments.push({ type: 'topology-figure', view: match[4] }); + } else if (match[0].includes('evidence-exports')) { + segments.push({ type: 'evidence-exports' }); + } else { + segments.push({ type: 'scanopy-demo' }); + } + + lastIndex = match.index + match[0].length; + } + + const remaining = html.slice(lastIndex).trim(); + if (remaining) { + segments.push({ type: 'html', content: remaining }); + } + + return { segments, vendorSlugs: [...new Set(allSlugs)] }; +} diff --git a/src/lib/fixtures/network-diagram-vendors.ts b/src/lib/fixtures/network-diagram-vendors.ts index 481ca42..502748b 100644 --- a/src/lib/fixtures/network-diagram-vendors.ts +++ b/src/lib/fixtures/network-diagram-vendors.ts @@ -80,12 +80,12 @@ export const vendors: Record = { discoveryNotes: 'Scan-on-demand (not continuous). Supports scheduled scans. Also discovers VMware and Hyper-V environments.', diagrams: - 'Multiple diagram types from a single scan (Layer 2, Layer 3, physical). Exports to Visio with auto-populated SmartShapes. Auto-layout with manual override.', + 'Multiple diagram types from a single scan (Layer 2, Layer 3, physical). Exports to Visio with auto-populated SmartShapes, plus PNG, PDF, and the SolarWinds Orion Network Atlas format. No SVG, no Mermaid, no embeddable or iframe output, and no API access to the map data. NTM is a standalone product that cannot be installed on the same server as the SolarWinds Platform, and its Network Atlas export is one-way.', pricingNotes: 'SolarWinds moved to subscription-only licensing across its product line in 2025. Renewal prices have reportedly increased significantly post-acquisition by Turn/River Capital.', whereItFits: 'Enterprise teams that need compliance-ready documentation with Visio exports. The Visio export alone makes it the default choice in organizations that standardize on Microsoft tools.', tradeOff: - 'Requires Windows (.NET 3.5 and 4.8). No web-based access, no embeddable maps, no API. NTM has received only maintenance updates since roughly 2016, with no new discovery or mapping features. SolarWinds staff confirmed in 2017 that there is no product roadmap. The product still works, but active development has shifted to SolarWinds Observability. SolarWinds disclosed a supply-chain compromise in 2020, though NTM is a separate, much simpler product.', + 'Requires Windows (.NET 3.5 and 4.8). No web-based access, no embeddable maps, no API. NTM has received only maintenance updates since roughly 2016, with no new discovery or mapping features. SolarWinds staff confirmed in 2017 that there is no product roadmap, and [NTM 2.2.x reaches full end-of-life in September 2026](https://documentation.solarwinds.com/en/success_center/ntm/content/release_notes/release_history.htm). Active development has shifted to SolarWinds Observability, whose NPM module carries its own topology views. NTM holds a [4.1 out of 5 on G2](https://www.g2.com/products/solarwinds-network-topology-mapper/reviews), where the most common request is exactly this integration; the product still works, but reviewers cite the lack of new features as the reason they look elsewhere. SolarWinds disclosed a supply-chain compromise in 2020, though NTM is a separate, much simpler product.', tradeOffLabel: 'Trade-offs', deployment: ['Desktop'], deploymentNotes: @@ -923,6 +923,14 @@ export const vendorSources: VendorSource[] = [ ]; export const vendorFAQs: VendorFAQ[] = [ + { + question: 'What are network documentation tools?', + answer: 'Network documentation tools keep a current, accurate record of your network: the devices on it, how they connect, and the services running on them. The automated kind discovers this directly from the network over protocols like SNMP, LLDP, and ARP and updates it on a schedule, instead of relying on someone to maintain a spreadsheet or diagram by hand. The output is usually a topology map plus a searchable inventory.' + }, + { + question: 'Do network documentation tools replace IT asset management or monitoring?', + answer: 'No. Documentation tools record what exists and how it connects. Monitoring watches device health and alerts on problems, and IT asset management tracks ownership, licensing, and procurement. The three overlap on the device inventory but answer different questions, and most teams run them alongside each other rather than one instead of another.' + }, { question: 'What is the difference between a network diagram tool and a network monitoring tool?', answer: 'A network diagram tool discovers devices and connections, then produces a visual topology map. A monitoring tool tracks device health, bandwidth, and alerts over time. Some monitoring platforms (Auvik, PRTG, Domotz, ManageEngine) include basic mapping as a feature. Dedicated diagram tools (Scanopy, SolarWinds NTM, NetBrain) focus entirely on producing accurate, shareable maps without bundling monitoring.' diff --git a/src/lib/guides/network-documentation-hipaa.md b/src/lib/guides/network-documentation-hipaa.md index b8f41ea..5e0b53b 100644 --- a/src/lib/guides/network-documentation-hipaa.md +++ b/src/lib/guides/network-documentation-hipaa.md @@ -98,4 +98,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views (physical, logical, workloads, applications) that updates on a schedule and exports for audit use. For a HIPAA covered entity, its job is to keep the systems-and-data-flow map your risk analysis depends on accurate without anyone maintaining it by hand. It runs alongside your monitoring, access control, and risk-management work, not in place of them. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Covered entities that also certify to a security standard or operate in the EU will find the same map doing double duty: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for NIS2](/guides/network-documentation-nis2) cover those controls. If you want the deeper category overview, see the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Covered entities that also certify to a security standard or operate in the EU will find the same map doing double duty: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for NIS2](/guides/network-documentation-nis2) cover those controls. For how Scanopy fits compliance work across standards, see the [compliance overview](/compliance), and for the deeper category overview, the [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools). diff --git a/src/lib/guides/network-documentation-iso27001.md b/src/lib/guides/network-documentation-iso27001.md index d353dad..beed0b9 100644 --- a/src/lib/guides/network-documentation-iso27001.md +++ b/src/lib/guides/network-documentation-iso27001.md @@ -97,4 +97,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views (physical, logical, workloads, applications) that updates on a schedule and exports for evidence. For an organization certifying to ISO 27001, its job is to keep the network diagram and asset inventory that A.8.20 and A.5.9 assume you maintain accurate on their own, so producing current documentation for the auditor stops being manual work. It runs alongside the rest of your ISMS, not in place of it. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support, and [pricing](/pricing) covers the plan tiers. For neighboring obligations, see the guides to [network documentation for NIS2](/guides/network-documentation-nis2) and [network documentation for HIPAA](/guides/network-documentation-hipaa), and for the broader category, the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support, and [pricing](/pricing) covers the plan tiers. For neighboring obligations, see the guides to [network documentation for NIS2](/guides/network-documentation-nis2) and [network documentation for HIPAA](/guides/network-documentation-hipaa), for how Scanopy fits compliance work across standards, the [compliance overview](/compliance), and for the broader category, the [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools). diff --git a/src/lib/guides/network-documentation-nis2.md b/src/lib/guides/network-documentation-nis2.md index 52a0b62..23f3b3d 100644 --- a/src/lib/guides/network-documentation-nis2.md +++ b/src/lib/guides/network-documentation-nis2.md @@ -91,4 +91,4 @@ On self-hosting: the Community and commercial self-hosted editions run entirely Scanopy is network documentation software: a lightweight daemon discovers your hosts, services, interfaces, topology, and application dependencies, then builds an interactive map with four views that updates on a schedule and exports for evidence. For an entity under NIS2, its job is to keep the asset inventory and network map that the risk-analysis, asset-management, and continuity measures assume you maintain accurate on their own, so producing current documentation stops being manual work. It runs alongside the rest of your risk-management program, not in place of it. -The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Entities that fall under more than one regime map much of the same evidence twice: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for HIPAA](/guides/network-documentation-hipaa) cover those controls. For the broader category, see the guide to [network documentation software](/guides/network-documentation-software). +The [Community Edition](/community) is free and self-hosted. The [commercial editions](/commercial) remove the seat and network limits and add support. Entities that fall under more than one regime map much of the same evidence twice: the guides to [network documentation for ISO 27001](/guides/network-documentation-iso27001) and [network documentation for HIPAA](/guides/network-documentation-hipaa) cover those controls. For how Scanopy fits compliance work across standards, see the [compliance overview](/compliance), and for the broader category, the [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools). diff --git a/src/lib/guides/network-documentation-software.md b/src/lib/guides/network-documentation-software.md deleted file mode 100644 index f1809d5..0000000 --- a/src/lib/guides/network-documentation-software.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: 'Network Documentation Software (2026): Automated & Open Source' -description: 'What network documentation software does, how automated discovery beats manual templates, and the open-source options. A practical guide for IT teams keeping infrastructure documented.' -keyword: network documentation software -slug: network-documentation-software -date: 2026-07-05 -dateModified: 2026-07-14 -tldr: "Network documentation software keeps a current record of what's on your network: devices, connections, and services. The useful kind discovers this automatically instead of asking you to type it into a spreadsheet or redraw a diagram. This covers what the category does, how automated tools differ from manual templates, the open-source options, and where the lines with monitoring and ITAM actually fall." -ctaHeading: Documentation that keeps itself current -ctaDescription: 'Scanopy discovers your network and builds a living topology map on a schedule. Flat pricing regardless of host count. It works alongside whatever monitoring and asset tools you already run.' -faq: - - question: What is network documentation software? - answer: Network documentation software keeps a current, accurate record of your network. The devices on it, how they're connected, and what services run on them. The automated kind discovers this directly from the network and keeps it updated on a schedule, instead of relying on someone to maintain a spreadsheet or diagram by hand. - - question: What is the best open-source network documentation software? - answer: For discovery-based documentation, Scanopy Community Edition (AGPL-3.0, self-hosted) and NetDisco (Layer 2 topology and inventory) are the main options. NetBox is open source as well and, with its NetBox Discovery agent, can scan your live network too; what it doesn't do natively is visualize topology (that comes from community plugins), so it's a structured source of truth rather than a map. - - question: Is network documentation software the same as network monitoring? - answer: No. Documentation software records what exists and how it connects; monitoring watches device health and alerts you to problems. They solve different problems and most teams run both. A documentation tool complements a monitoring platform, it doesn't replace it. - - question: How is automated network documentation different from a template? - answer: A template is a blank structure you fill in and maintain by hand, so it goes stale as the network changes. Automated network documentation discovers the current state from the network itself using protocols like SNMP and LLDP, and rescans on a schedule, so the record stays accurate without manual updates. - - question: Does network documentation software replace IT asset management? - answer: No. ITAM tracks ownership, lifecycle, licensing, and procurement. Network documentation tracks what's on the network and how it's connected. They overlap on the device inventory but answer different questions, and they're typically used together. - - question: How much does network documentation software cost? - answer: The pricing model matters more than the sticker price. Monitoring-adjacent tools charge per device, which penalizes documenting everything, and documentation's value is completeness. Open-source options like Scanopy Community Edition and NetDisco are free to self-host. Scanopy's commercial editions are priced flat regardless of host count, so documenting your whole network costs the same as documenting part of it. ---- - -Network documentation software keeps an accurate, current record of your network: what devices exist, how they connect, and what runs on them. The category splits into two kinds. One asks you to enter and maintain that record by hand. The other discovers it from the network itself and keeps it current automatically. The manual kind is out of date as soon as the network changes. - -## What does network documentation software do? - -Good network documentation software answers three questions and keeps the answers current: - -- **What is on the network?** Hosts, their IP and MAC addresses, hostnames, and vendors. The deeper tools also fingerprint the services running on each host, so a box isn't just an IP, it's "the machine running Postgres, Nginx, and a stack of containers." -- **How is it connected?** Physical links between switches, routers, and hosts, plus the logical layer of subnets and VLANs. This is the part manual diagrams get wrong first, because cabling and topology change without anyone updating the drawing. -- **What changed?** A record you can trust over time, not a file someone last touched eighteen months ago. - -The output is usually a topology map plus a searchable inventory. Documentation software derives that data from discovery; a drawing tool derives it from memory. If your emphasis is on reading the topology itself, switching between the physical, logical, and dependency layers, rather than keeping an accurate record, that is the visualization angle, covered in our [network mapping software](/guides/network-mapping-software) guide. - -## Automated vs manual: why templates go stale - -A [network documentation template](/blog/network-documentation-template) is a reasonable place to start. It gives you structure. But the moment you fill it in, it starts going stale, because networks change constantly and spreadsheets are updated only when someone remembers to. The honor system does not work for documentation maintenance. - -Automated network documentation solves this by taking humans out of the update loop. Software queries the network directly using protocols your devices already implement (SNMP, LLDP, CDP, and ARP), maps what it finds, and rescans on a schedule. No drawing, no data entry, no discipline required. Here is a deeper look at [how automated network documentation works](/blog/automated-network-documentation), and if you want to compare specific tools by discovery method and pricing, we put together a rundown of the [best automated network diagram tools](/comparisons/best-automated-network-diagram-tools). - -The practical test for any tool marketed as "automated": does it discover your network and produce the documentation without you drawing anything? Some tools marketed as documentation software are diagram editors with a stencil library. - -## Documentation, monitoring, and ITAM are not the same thing - -This category gets muddy because three different kinds of tools all touch your device inventory. They are not substitutes. - -| Tool type | Core job | Priced for | -| -------------------------- | --------------------------------------------------------------------- | ---------------------------- | -| Network documentation | Keep an accurate map and inventory of what exists and how it connects | Completeness (whole network) | -| Network monitoring | Watch device health, alert on problems, track performance over time | Per-device attention | -| IT asset management (ITAM) | Track ownership, lifecycle, licensing, and procurement of assets | Asset records | - -Most teams need more than one of these. Documentation software tells you the network's shape; monitoring tells you when part of it is on fire; ITAM tells you who owns the laptop and when its warranty ends. The right setup runs them alongside each other, not one instead of another. A documentation tool that tries to also be your monitoring platform usually does neither part well. - -## Open-source network documentation software - -Open-source options matter for two reasons: self-hosting on a network that can't phone home, and avoiding per-seat or per-device licensing. - -- **[Scanopy Community Edition](/community)** is AGPL-3.0 (an OSI-approved open-source license). It's the documentation-focused, self-hosted path: automated discovery, per-host service detection, and, unlike the other open-source options here, four switchable views of the same scan (physical L2, logical L3, workloads, and applications). Most OSS tools give you a single view, usually L2 topology. Getting those four views from a single scan is the main reason to reach for Scanopy CE over NetDisco or a NetBox plugin. -- **NetDisco** is a long-standing open-source tool for Layer 2 topology discovery and device inventory, strong on switch port mapping. -- **NetBox** is open source too (Apache-2.0), and its NetBox Discovery agent (also open source) can actively scan your live network, so it's not limited to what you type in. Its job is still different from Scanopy's: NetBox is a structured source of truth (DCIM plus IPAM) behind a REST and GraphQL API, and it has no native topology visualization (maps come from community plugins like netbox-topology-views). It's the data model; Scanopy is the map. The two pair well together. - -If your requirement is "self-hosted and free to start," the Community Edition covers the documentation use case. If you outgrow it, the commercial editions add [team features and support](/commercial) without changing the pricing model. - -## Per-device pricing makes complete documentation the most expensive option - -Documentation's value is completeness. A map that covers 80% of your devices isn't 80% as useful, it's close to useless, because the undocumented 20% is where the outage will be. Per-device pricing works for monitoring, where you can watch critical devices closely and sample the rest. For documentation it makes complete coverage the most expensive option, so documenting more of your network costs you more. - -That's why Scanopy prices [flat regardless of host count](/pricing). Pricing documentation independently from monitoring also means you can switch monitoring platforms later without losing your maps. - -## How do you choose network documentation software? - -- **You want the network documented without maintaining it by hand:** an automated, discovery-based tool. That is what the category is for. -- **You need it self-hosted or free to start:** [Scanopy Community](/community) or NetDisco. -- **You already have monitoring and just need current documentation:** a dedicated documentation tool that runs alongside it, priced independently. -- **You need a structured source of truth for automation to consume:** NetBox (its data model feeds Ansible, Nornir, Terraform), with its own NetBox Discovery agent or another feeder keeping it current. Pair it with a visualization tool, since NetBox has no native maps. -- **You only need a one-time diagram for a presentation:** a manual tool like draw.io. Don't buy documentation software for a single drawing. -- **An auditor or regulator is the reason you are looking:** start from the control you have to satisfy, since that determines what the documentation has to show. Separate guides map the requirements for [NIS2](/guides/network-documentation-nis2), [ISO 27001](/guides/network-documentation-iso27001), and [HIPAA](/guides/network-documentation-hipaa) to the evidence each one expects. - -## Scanopy documents the network and runs alongside monitoring and ITAM - -Scanopy is network documentation software, not a monitoring or asset platform. A lightweight daemon discovers your hosts, services, interfaces, and topology, then builds an interactive map with four views of the same scan (physical L2, logical L3, workloads, and applications). The map updates on a schedule. You can embed it in a wiki or intranet via iframe, share a read-only link that stays current as the network rescans, or export a snapshot as an image (PNG, SVG, PDF, HTML), as diagram markup (Mermaid, Confluence), or as CSV data. It runs alongside whatever monitoring and ITAM tools you already have. diff --git a/src/lib/guides/network-mapping-software.md b/src/lib/guides/network-mapping-software.md index 548a5f1..18b1337 100644 --- a/src/lib/guides/network-mapping-software.md +++ b/src/lib/guides/network-mapping-software.md @@ -86,7 +86,7 @@ Mapping and monitoring get confused because both touch your devices, but they dr | Network monitoring | The state: device health, metrics, alerts | Something is down and you need to know first | | Manual diagram tool | Whatever you draw on the canvas | You need a one-off picture for a slide | -Mapping shows you the shape of the network; monitoring tells you when part of that shape is on fire. Most teams need both, run alongside each other. A mapping tool that also tries to be your monitoring platform usually does neither part well. If your problem is less "I need to see how this connects" and more "we have no reliable record of what we have," that is the record-keeping angle, covered in our [network documentation software](/guides/network-documentation-software) guide. +Mapping shows you the shape of the network; monitoring tells you when part of that shape is on fire. Most teams need both, run alongside each other. A mapping tool that also tries to be your monitoring platform usually does neither part well. If your problem is less "I need to see how this connects" and more "we have no reliable record of what we have," that is the record-keeping angle, and the [network documentation tools comparison](/comparisons/best-automated-network-diagram-tools) covers the tools built for it. ## Open-source network mapping tools diff --git a/src/lib/guides/open-source-network-documentation.md b/src/lib/guides/open-source-network-documentation.md new file mode 100644 index 0000000..a15965b --- /dev/null +++ b/src/lib/guides/open-source-network-documentation.md @@ -0,0 +1,52 @@ +--- +title: 'Open Source Network Documentation Tools (2026): Self-Hosted & Free' +description: 'The open-source, self-hosted network documentation and diagram tools compared: Scanopy CE, NetDisco, LibreNMS, NetBox, and more, by license, what they discover, and what they map.' +keyword: open source network documentation +slug: open-source-network-documentation +date: 2026-07-21 +dateModified: 2026-07-21 +tldr: "The open-source options fall into two groups: tools that discover your network and document it for you (Scanopy Community Edition, NetDisco, LibreNMS, NetBox), and drawing tools you fill in by hand (draw.io). If you want documentation that stays current, you want the discovery kind. This compares the self-hosted, free options by license, what each one discovers, and what it actually maps, so you can pick one to run yourself." +ctaHeading: Free to self-host, and it draws the map itself +ctaDescription: 'Scanopy Community Edition is AGPL-3.0 and self-hosted. One daemon discovers your network and renders an interactive topology map that updates on a schedule, and it runs alongside whatever monitoring you already have.' +faq: + - question: What are the open-source network documentation tools? + answer: The main self-hosted, open-source options are Scanopy Community Edition (AGPL-3.0), which discovers the network and renders an interactive topology map; NetDisco (BSD), strong on Layer 2 switch-port mapping; LibreNMS (GPL-3.0), a monitoring platform with discovery and basic maps; and NetBox (Apache-2.0), a source-of-truth database that can scan with its Discovery agent but has no native map. draw.io (Apache-2.0) is open source too, but it is a manual drawing tool with no discovery. + - question: Is there open-source network diagram software that draws the diagram automatically? + answer: Yes. Open-source network diagram software splits into two kinds. draw.io is free and open source but you draw every box by hand. Scanopy Community Edition, NetDisco, and LibreNMS discover the network over SNMP, LLDP, and ARP and generate the diagram from what actually exists, so it does not go stale the way a hand-drawn one does. If you want a diagram that stays current on its own, choose a discovery-based tool. + - question: Is there a free, self-hosted NetBox alternative? + answer: It depends on what you use NetBox for. NetBox is a structured source-of-truth database with no native topology map, so teams often pair it with a visualization tool. If what you actually want is a self-hosted tool that discovers the network and draws the map for you, Scanopy Community Edition (AGPL-3.0) does that in one daemon. NetDisco is another self-hosted option focused on Layer 2. See the NetBox comparison for where each fits. + - question: Can I self-host network documentation for free? + answer: Yes. Scanopy Community Edition, NetDisco, LibreNMS, and NetBox are all free and self-hosted. You run them on your own infrastructure, which means you also run the database, updates, and backups. That is the trade-off of self-hosting: no license cost, but the operational upkeep is yours. Scanopy's commercial editions exist for teams that want the same tool without running the stack themselves. + - question: Which open-source network documentation tool is best? + answer: There is no single best; it depends on the job. For an interactive topology map with switchable views, Scanopy Community Edition. For deep Layer 2 switch-port mapping, NetDisco. For monitoring with discovery attached, LibreNMS. For a structured inventory database that feeds automation, NetBox. Most of these do one thing well, so match the tool to the question you need answered. + - question: What license are these open-source tools under? + answer: They differ, and the difference matters if you plan to modify or redistribute. Scanopy Community Edition is AGPL-3.0, NetDisco is BSD, LibreNMS is GPL-3.0, and NetBox and draw.io are Apache-2.0. All are OSI-approved. Nmap is source-available under the NPSL, which is not an OSI-approved open-source license, so treat it separately if license terms are a hard requirement. +--- + +If you want your network documented but you also want to self-host it and pay nothing for the license, you have several open-source options. The catch is that they are not interchangeable. Some discover your network and draw the map for you; one of the popular ones is a drawing tool you fill in by hand; and one is a database with no map at all. This guide sorts them out so you can pick the one that matches what you need. + +## The open-source options split into discovery tools and drawing tools + +This is the distinction everything else follows from. A discovery tool queries the network over protocols the devices already implement (SNMP, LLDP, ARP) and generates the documentation from what it finds. A drawing tool gives you a canvas and you place every box yourself. Both can be open source and free. Only the first kind stays current on its own. + +draw.io is the drawing tool people reach for, and it is a capable manual diagramming tool. But [a diagram you draw by hand is wrong the moment the network changes](/blog/network-diagrams-wrong), which is the same problem [a spreadsheet template has](/blog/network-documentation-template). If your goal is documentation that stays current, you want a discovery tool. The rest of this guide is mostly about those. + + + +## Every tool here is free to self-host, but the licenses differ + +All of these cost nothing to run. What differs is the license, which matters if you plan to modify the code or ship it inside something else. Scanopy Community Edition is AGPL-3.0, NetDisco is BSD, LibreNMS is GPL-3.0, and both NetBox and draw.io are Apache-2.0. Those are all OSI-approved open-source licenses. Nmap is a special case: it is source-available under the NPSL, which is not OSI-approved, so if a strict open-source license is a hard requirement for you, keep Nmap in a separate column in your head. + +## Self-hosting is free on license and costs you the operations + +Running one of these yourself means no license cost. It also means you run the database, apply the updates, and own the backups. The trade is operational: you spend engineering time instead of license money. For a homelab or a team that already runs its own stack, that is a fair trade. If you would rather not run the stack yourself, [Scanopy Cloud](/pricing) is hosted and managed. If you want to stay self-hosted but past the Community Edition's single-network limit, the [commercial license](/commercial) removes those caps. + +## NetBox is open source but does not draw a map + +NetBox comes up constantly in this search, so it is worth being precise about what it is. NetBox is a source-of-truth database: a structured, self-hosted inventory of your network that automation tools consume. It can discover the network with its own Discovery agent, but it has no native topology map. Visualization is plugin-based ([netbox-topology-views](https://github.com/netbox-community/netbox-topology-views) is the common one), not part of the core product. If what you want is a documented inventory that feeds Ansible or Terraform, that is what NetBox is for. If what you want is to look at your network, you want a tool that renders the map itself. The [NetBox comparison](/comparisons/vs/netbox) covers where the two overlap and where they do not. + +## Scanopy Community Edition discovers the network and renders the map itself + +[Scanopy Community Edition](/community) is AGPL-3.0 and self-hosted. One lightweight daemon discovers your hosts, [services](/services), interfaces, and topology, then builds an interactive map with four switchable views (physical, logical, workloads, applications) that updates on a schedule. You can embed it in a wiki or intranet via iframe, share a read-only link that stays current as the network rescans, or export a snapshot as an image (PNG, SVG, PDF, HTML), as diagram markup (Mermaid, Confluence), or as CSV data. It is the discovery-and-map option in one tool, free to run yourself. + +The open-source tools here are all narrower than the full field. If you want to weigh them against the commercial tools too, with pricing and discovery method side by side, see the full comparison of [network documentation tools](/comparisons/best-automated-network-diagram-tools). diff --git a/src/lib/guides/snmp-network-topology-mapping.md b/src/lib/guides/snmp-network-topology-mapping.md index 5177968..085dcc6 100644 --- a/src/lib/guides/snmp-network-topology-mapping.md +++ b/src/lib/guides/snmp-network-topology-mapping.md @@ -256,7 +256,7 @@ Everything above is what Scanopy does under the hood, every time it runs a scan. Configuring it takes one input the tutorial didn't: your SNMP credentials, so the daemon can query your gear the same way `snmpwalk -c netdefault` just did. You set these up when you configure a daemon to install. A credential is either a community string (SNMPv1 and v2c) or a username with auth and privacy passphrases (SNMPv3), and you give it one of two scopes: a broadcast credential that's tried against every host during a scan, or a per-host credential pinned to specific IP addresses. When a scan runs, Scanopy attempts each credential as configured, and for every host where one succeeds, it persists that credential as the host's assigned credential and reuses it on later discovery runs. The first scan does the trial-and-error you'd otherwise do by hand with `-c`; every scan after that reuses the credential each device accepted. You can also create credentials outside the daemon setup at any time and assign them to hosts or set them network-wide. -The difference is scale and repetition. Scanopy does this across your entire network in minutes, repeats it on a schedule, and renders the result as an interactive map instead of an ASCII tree. Plenty of tools automate this SNMP walking, and we [compared the best automated network diagram tools](/comparisons/best-automated-network-diagram-tools) on discovery method, live updates, and pricing if you want to see how they stack up. +The difference is scale and repetition. Scanopy does this across your entire network in minutes, repeats it on a schedule, and renders the result as an interactive map instead of an ASCII tree. Plenty of tools automate this SNMP walking, and we [compared network documentation tools](/comparisons/best-automated-network-diagram-tools) on discovery method, live updates, and pricing if you want to see how they stack up. Start with a single device. Here is the same SNMP data you walked by hand, the system identity from Step 1 and the interface list from Step 2, pulled and structured automatically for switch-core-01: diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 370c446..489b279 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -390,13 +390,14 @@ start at {startingPrice}/month. Read how automated network documentation works, compare the + >, compare best automated network diagram software, browse the guide to - network documentation softwarenetwork documentation tools, browse the + open-source options, or review our security practices.

    diff --git a/src/routes/blog/[slug]/+page.server.ts b/src/routes/blog/[slug]/+page.server.ts index 33428aa..9415150 100644 --- a/src/routes/blog/[slug]/+page.server.ts +++ b/src/routes/blog/[slug]/+page.server.ts @@ -2,6 +2,7 @@ import { marked, Renderer } from 'marked'; import { error } from '@sveltejs/kit'; import { vendors as allVendors } from '$lib/fixtures/network-diagram-vendors'; import { externalizeLinks } from '$lib/server/externalize-links'; +import { splitContentSegments } from '$lib/content/contentSegments'; import type { Vendor } from '$lib/types'; interface Heading { @@ -31,59 +32,6 @@ interface BlogPost { wordCount: number; } -type ContentSegment = - | { type: 'html'; content: string } - | { type: 'vendor-inline-table'; vendorSlugs: string[]; columns: string[] } - | { type: 'scanopy-demo' } - | { type: 'customer-quote'; id: string }; - -const DEFAULT_INLINE_COLUMNS = ['name', 'discovery', 'pricing', 'bestFor']; - -// Splits rendered markdown on inline component markers so they render as Svelte components -// instead of raw HTML: ``, `` -// (a theme-aware live map embed), and `` (a customer quote paired with the -// customer's logo). Returns null when the post has none of these markers. -function splitBlogSegments( - html: string -): { segments: ContentSegment[]; vendorSlugs: string[] } | null { - const markerRegex = - //g; - if (!markerRegex.test(html)) return null; - - const segments: ContentSegment[] = []; - const allSlugs: string[] = []; - let lastIndex = 0; - markerRegex.lastIndex = 0; - let match: RegExpExecArray | null; - - while ((match = markerRegex.exec(html)) !== null) { - const before = html.slice(lastIndex, match.index).trim(); - if (before) { - segments.push({ type: 'html', content: before }); - } - - if (match[1]) { - const vendorSlugs = match[1].split(',').filter(Boolean); - const columns = match[2] ? match[2].split(',').filter(Boolean) : DEFAULT_INLINE_COLUMNS; - allSlugs.push(...vendorSlugs); - segments.push({ type: 'vendor-inline-table', vendorSlugs, columns }); - } else if (match[3]) { - segments.push({ type: 'customer-quote', id: match[3] }); - } else { - segments.push({ type: 'scanopy-demo' }); - } - - lastIndex = match.index + match[0].length; - } - - const remaining = html.slice(lastIndex).trim(); - if (remaining) { - segments.push({ type: 'html', content: remaining }); - } - - return { segments, vendorSlugs: [...new Set(allSlugs)] }; -} - function parseFrontmatter(content: string): { frontmatter: Record; body: string } { const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (!match) { @@ -206,7 +154,7 @@ export async function load({ params }) { wordCount }; - const parsed = splitBlogSegments(htmlContent); + const parsed = splitContentSegments(htmlContent); if (parsed) { const filteredVendors: Record = {}; for (const vs of parsed.vendorSlugs) { diff --git a/src/routes/blog/[slug]/+page.svelte b/src/routes/blog/[slug]/+page.svelte index 196bd5f..8ac00f3 100644 --- a/src/routes/blog/[slug]/+page.svelte +++ b/src/routes/blog/[slug]/+page.svelte @@ -9,6 +9,7 @@ import CustomerQuote from '$lib/components/CustomerQuote.svelte'; import { getFAQPageSchema } from '$lib/schemas'; import type { Vendor } from '$lib/types'; + import type { ContentSegment } from '$lib/content/contentSegments'; interface Heading { id: string; @@ -37,12 +38,6 @@ wordCount: number; } - type ContentSegment = - | { type: 'html'; content: string } - | { type: 'vendor-inline-table'; vendorSlugs: string[]; columns: string[] } - | { type: 'scanopy-demo' } - | { type: 'customer-quote'; id: string }; - interface PageData { post: BlogPost; headings: Heading[]; diff --git a/src/routes/comparisons/[slug=alternatives]/+page.svelte b/src/routes/comparisons/[slug=alternatives]/+page.svelte index 58595e7..2e97fd9 100644 --- a/src/routes/comparisons/[slug=alternatives]/+page.svelte +++ b/src/routes/comparisons/[slug=alternatives]/+page.svelte @@ -69,7 +69,7 @@ const breadcrumbSchema = getBreadcrumbListSchema([ { name: 'Comparisons', url: 'https://scanopy.net/comparisons' }, - { name: 'Best automated network diagram tools', url: mainComparison }, + { name: 'Network documentation tools', url: mainComparison }, { name: data.title, url: canonical } ]); @@ -236,7 +236,7 @@ >Scanopy vs {data.vendorName} head-to-head. For all 13 tools side by side, see the full comparison of automated network diagram toolsfull network documentation tools comparison.

    diff --git a/src/routes/comparisons/[slug]/+page.server.ts b/src/routes/comparisons/[slug]/+page.server.ts index 9e9161d..9b0a4a6 100644 --- a/src/routes/comparisons/[slug]/+page.server.ts +++ b/src/routes/comparisons/[slug]/+page.server.ts @@ -162,7 +162,7 @@ function generateItemListSchema() { return { '@context': 'https://schema.org', '@type': 'ItemList', - name: 'Best Automated Network Diagram Tools (2026)', + name: 'Best Network Documentation Tools (2026)', numberOfItems: items.length, itemListElement: items.map((item, i) => ({ '@type': 'ListItem', diff --git a/src/routes/comparisons/[slug]/+page.svelte b/src/routes/comparisons/[slug]/+page.svelte index f93914d..b33af3c 100644 --- a/src/routes/comparisons/[slug]/+page.svelte +++ b/src/routes/comparisons/[slug]/+page.svelte @@ -94,8 +94,8 @@ }, about: { '@type': 'Thing', - name: 'Automated network diagram tools', - description: 'Software that discovers network topology and generates diagrams automatically' + name: 'Network documentation tools', + description: 'Software that discovers a network and keeps its documentation and topology current automatically' }, image: `https://scanopy.net${data.post.image}` }; diff --git a/src/routes/comparisons/scanopy-alternatives/+page.svelte b/src/routes/comparisons/scanopy-alternatives/+page.svelte index 214303d..678f3c5 100644 --- a/src/routes/comparisons/scanopy-alternatives/+page.svelte +++ b/src/routes/comparisons/scanopy-alternatives/+page.svelte @@ -62,7 +62,7 @@ const breadcrumbSchema = getBreadcrumbListSchema([ { name: 'Comparisons', url: 'https://scanopy.net/comparisons' }, - { name: 'Best automated network diagram tools', url: mainComparison }, + { name: 'Network documentation tools', url: mainComparison }, { name: data.title, url: canonical } ]); @@ -208,7 +208,7 @@

    For all 13 tools side by side, see the full comparison of automated network diagram toolsfull network documentation tools comparison, or browse the head-to-head and alternatives comparisons.

    diff --git a/src/routes/comparisons/vs/[slug]/+page.svelte b/src/routes/comparisons/vs/[slug]/+page.svelte index e6b4ef3..770f753 100644 --- a/src/routes/comparisons/vs/[slug]/+page.svelte +++ b/src/routes/comparisons/vs/[slug]/+page.svelte @@ -76,7 +76,7 @@ { '@type': 'ListItem', position: 2, - name: `Best automated network diagram tools`, + name: `Network documentation tools`, item: mainComparison }, { '@type': 'ListItem', position: 3, name: data.title, item: canonical } @@ -177,7 +177,7 @@ best {data.vendorName} alternatives.{/if} For all 13 tools side by side, see the full comparison of automated network diagram toolsfull network documentation tools comparison.

    diff --git a/src/routes/compliance/+page.svelte b/src/routes/compliance/+page.svelte new file mode 100644 index 0000000..240a216 --- /dev/null +++ b/src/routes/compliance/+page.svelte @@ -0,0 +1,294 @@ + + + + {title} + + + + + + + + + + + + + {@html ``} + {@html ``} + + +
    + +
    +
    +

    + Audit-ready network documentation,without the effort. +

    +

    + NIS2, ISO 27001, and HIPAA all expect network documentation that is accurate today, not last + year. Scanopy discovers your network and keeps the map and inventory current on a schedule, + so the evidence is ready before the auditor asks. +

    + +
    +
    + + +
    +
    +
    +

    + What NIS2, ISO 27001, and HIPAA expect, and what Scanopy produces +

    +

    + Each one expects documentation that reflects the network as it is now, plus evidence that + it stayed that way over time. +

    +
    +
    + {#each standards as s (s.name)} + + {s.name} + {s.control} + {s.blurb} + Read the guide → + + {/each} +
    +
    +
    + + +
    +
    +
    +

    + Give each control the evidence it asks for +

    +

    + One scan produces four topology views, generated from the live network rather than redrawn + by hand. Each answers a question an assessor asks: what's on the network, how it's + segmented, what runs where, and how data moves between systems. +

    +
    +
    + +
    +
    +
    + + +
    +
    +
    +

    + Hand auditors evidence in whatever form they want +

    +

    + Producing the evidence stops being a scramble before each assessment. The current map is + always a click from a document, a live embed, a shared link, or a dated record. +

    +
    + +
    +
    + + +
    +
    +

    + Self-hosted keeps the data on your infrastructure +

    +

    + Run Scanopy fully self-hosted and the network data never leaves your infrastructure. How + Scanopy handles data in each deployment model, with subprocessors and security practices, is + on the security page. +

    +
    +
    + + +
    +
    + +
    +
    + + +
    +
    +
    +

    + See it on your own network. +

    + +

    + Prefer to self-host? The + commercial editions + run in your own environment with support, and + pricing covers the tiers. +

    +
    +
    +
    +
    + + diff --git a/src/routes/guides/[slug]/+page.server.ts b/src/routes/guides/[slug]/+page.server.ts index 87fb170..d7c0288 100644 --- a/src/routes/guides/[slug]/+page.server.ts +++ b/src/routes/guides/[slug]/+page.server.ts @@ -2,6 +2,7 @@ import { marked, Renderer } from 'marked'; import { error } from '@sveltejs/kit'; import { vendors as allVendors } from '$lib/fixtures/network-diagram-vendors'; import { externalizeLinks } from '$lib/server/externalize-links'; +import { splitContentSegments } from '$lib/content/contentSegments'; import type { Vendor } from '$lib/types'; interface Heading { @@ -31,67 +32,6 @@ interface BlogPost { wordCount: number; } -type ContentSegment = - | { type: 'html'; content: string } - | { type: 'vendor-inline-table'; vendorSlugs: string[]; columns: string[] } - | { type: 'scanopy-demo' } - | { type: 'customer-quote'; id: string } - | { type: 'evidence-exports' } - | { type: 'topology-figure'; view: string }; - -const DEFAULT_INLINE_COLUMNS = ['name', 'discovery', 'pricing', 'bestFor']; - -// Splits rendered markdown on inline component markers so they render as Svelte components -// instead of raw HTML: ``, `` -// (a theme-aware live map embed), `` (a customer quote paired with the -// customer's logo), `` (the shared exports/embed/share block used across -// the compliance guides), and `` (a shared, -// captioned topology screenshot). Returns null when the post has none of these markers. -function splitBlogSegments( - html: string -): { segments: ContentSegment[]; vendorSlugs: string[] } | null { - const markerRegex = - //g; - if (!markerRegex.test(html)) return null; - - const segments: ContentSegment[] = []; - const allSlugs: string[] = []; - let lastIndex = 0; - markerRegex.lastIndex = 0; - let match: RegExpExecArray | null; - - while ((match = markerRegex.exec(html)) !== null) { - const before = html.slice(lastIndex, match.index).trim(); - if (before) { - segments.push({ type: 'html', content: before }); - } - - if (match[1]) { - const vendorSlugs = match[1].split(',').filter(Boolean); - const columns = match[2] ? match[2].split(',').filter(Boolean) : DEFAULT_INLINE_COLUMNS; - allSlugs.push(...vendorSlugs); - segments.push({ type: 'vendor-inline-table', vendorSlugs, columns }); - } else if (match[3]) { - segments.push({ type: 'customer-quote', id: match[3] }); - } else if (match[4]) { - segments.push({ type: 'topology-figure', view: match[4] }); - } else if (match[0].includes('evidence-exports')) { - segments.push({ type: 'evidence-exports' }); - } else { - segments.push({ type: 'scanopy-demo' }); - } - - lastIndex = match.index + match[0].length; - } - - const remaining = html.slice(lastIndex).trim(); - if (remaining) { - segments.push({ type: 'html', content: remaining }); - } - - return { segments, vendorSlugs: [...new Set(allSlugs)] }; -} - function parseFrontmatter(content: string): { frontmatter: Record; body: string } { const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); if (!match) { @@ -214,7 +154,7 @@ export async function load({ params }) { wordCount }; - const parsed = splitBlogSegments(htmlContent); + const parsed = splitContentSegments(htmlContent); if (parsed) { const filteredVendors: Record = {}; for (const vs of parsed.vendorSlugs) { diff --git a/src/routes/guides/[slug]/+page.svelte b/src/routes/guides/[slug]/+page.svelte index 1290662..41096cc 100644 --- a/src/routes/guides/[slug]/+page.svelte +++ b/src/routes/guides/[slug]/+page.svelte @@ -11,6 +11,7 @@ import TopologyFigure from '$lib/components/TopologyFigure.svelte'; import { getFAQPageSchema } from '$lib/schemas'; import type { Vendor } from '$lib/types'; + import type { ContentSegment } from '$lib/content/contentSegments'; interface Heading { id: string; @@ -39,14 +40,6 @@ wordCount: number; } - type ContentSegment = - | { type: 'html'; content: string } - | { type: 'vendor-inline-table'; vendorSlugs: string[]; columns: string[] } - | { type: 'scanopy-demo' } - | { type: 'customer-quote'; id: string } - | { type: 'evidence-exports' } - | { type: 'topology-figure'; view: string }; - interface PageData { post: BlogPost; headings: Heading[]; diff --git a/src/routes/services/+page.svelte b/src/routes/services/+page.svelte index 0d310c4..60245b0 100644 --- a/src/routes/services/+page.svelte +++ b/src/routes/services/+page.svelte @@ -108,7 +108,7 @@ field compares in our comparison of automated network diagram toolsnetwork documentation tools comparison. Want to compare plans? You can self-host free or get a diff --git a/src/routes/sitemap.xml/+server.ts b/src/routes/sitemap.xml/+server.ts index d71fd13..f37105c 100644 --- a/src/routes/sitemap.xml/+server.ts +++ b/src/routes/sitemap.xml/+server.ts @@ -51,6 +51,7 @@ export async function GET() { }, { loc: '/community', src: 'src/routes/community/+page.svelte' }, { loc: '/commercial', src: 'src/routes/commercial/+page.svelte' }, + { loc: '/compliance', src: 'src/routes/compliance/+page.svelte' }, { loc: '/press', src: 'src/routes/press/+page.svelte' }, { loc: '/security', src: 'src/routes/security/+page.svelte' }, { loc: '/privacy', src: 'src/routes/privacy/+page.svelte' }, diff --git a/static/_redirects b/static/_redirects index 7b47139..af95001 100644 --- a/static/_redirects +++ b/static/_redirects @@ -222,5 +222,10 @@ /topology-hero-light.webp /og/topology-hero-light.webp 301 /hero-topology-dark.webp /og/hero-topology-dark.webp 301 +# Guide consolidation (Jul 2026): network-documentation-software merged into the +# network documentation tools comparison, which now owns that category. +/guides/network-documentation-software /comparisons/best-automated-network-diagram-tools 301 +/guides/network-documentation-software/ /comparisons/best-automated-network-diagram-tools 301 + # 404 for unmatched paths (prevents Cloudflare Pages index.html fallback) /* /404.html 404 From 82585624a03e14d70b3336561c9c5db6445e2ed0 Mon Sep 17 00:00:00 2001 From: Maya Date: Wed, 22 Jul 2026 12:38:10 -0400 Subject: [PATCH 3/5] =?UTF-8?q?Update=20docs=20for=20the=20server-provisio?= =?UTF-8?q?ned=20daemon=20model=20(v0.17.4=20=E2=86=92=200.17.5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Docs were last refreshed against 2026-07-15; everything after that was undocumented, including the change to server-provisioned daemon identity. Docs still told users to create a daemon API key by hand, which now yields a daemon the server rejects as daemon_not_provisioned. - Add reference/daemon-identity: provisioning, 1:1 key binding, the 0.17.5 version floor, legacy shared-key coexistence, fixed vs editable fields. Linked from the pages that previously restated a slice of this. - managing-daemons: describe install methods instead of reproducing the commands (the server assembles them and is their source of truth); add Reconfiguring a Daemon and Binding a Key to a Legacy Daemon; rewrite Updating Daemon Properties around the management modal; note the new uninstall dispositions and log-file purge. - daemon-status: document Awaiting Connection, which every provisioned daemon passes through. Fix two stale facts while here — the priority order was listed backwards, and a healthy daemon does show a tag. - Correct the identity claims in organization, troubleshooting-setup, planning-daemon-deployment, multiple-daemons, and both self-hosted pages. - daemon-configuration: add --config-dir and the installed-service config location; drop the --concurrent-scans section (no such flag) and the v0.15 deprecation block that promised removal in v0.16. - Document Max Discovery Duration and the partial-run warning; rewrite the FD-limit guidance now that the daemon raises its own soft limit; record the SNMP probe/query timeouts and the ifTable truncation warning. - Note that the generated install command no longer carries --name, so a multi-instance host must add it or every instance overwrites the last. - Drop the Windows MSI from the docs; it isn't signed yet. --- .../guides/networking/multiple-daemons.mdx | 17 ++-- .../docs/reference/daemon-configuration.mdx | 69 +++------------ .../docs/reference/daemon-identity.mdx | 84 +++++++++++++++++++ docs/content/docs/reference/daemon-status.mdx | 25 +++++- docs/content/docs/reference/limitations.mdx | 4 +- docs/content/docs/reference/meta.json | 1 + docs/content/docs/reference/snmp.mdx | 11 ++- .../self-hosted-server/troubleshooting.mdx | 2 +- .../docs/self-hosted-server/upgrading.mdx | 10 ++- .../setting-up-daemons/managing-daemons.mdx | 71 +++++++++++----- .../planning-daemon-deployment.mdx | 4 +- .../scan-performance.mdx | 54 +++++------- .../troubleshooting-scans/snmp.mdx | 16 ++++ .../troubleshooting-setup.mdx | 5 +- docs/content/docs/using-scanopy/discovery.mdx | 8 ++ .../docs/using-scanopy/organization.mdx | 6 +- docs/src/components/status-tag.tsx | 1 + src/lib/fixtures/daemon-config.json | 10 +++ 18 files changed, 259 insertions(+), 139 deletions(-) create mode 100644 docs/content/docs/reference/daemon-identity.mdx diff --git a/docs/content/docs/guides/networking/multiple-daemons.mdx b/docs/content/docs/guides/networking/multiple-daemons.mdx index 57bdcb9..a9e3e52 100644 --- a/docs/content/docs/guides/networking/multiple-daemons.mdx +++ b/docs/content/docs/guides/networking/multiple-daemons.mdx @@ -82,18 +82,15 @@ SCANOPY_DAEMON_PORT=60073 ## Setting up multiple daemons -Create a separate daemon entry in **Discover > Scan > Daemons** for each instance. Each daemon gets its own API key and can target the same or different networks. During setup, use the **Advanced** tab to configure interface restrictions under Network Discovery. The app provides platform-specific install and run commands with your settings included. +Create a separate daemon entry in **Discover > Scan > Daemons** for each instance. Each is provisioned with its own API key and can target the same or different networks. During setup, use the **Advanced** tab to set that instance's interface restrictions, bind address, and port under Network Discovery — they're baked into the install artifact the wizard gives you, so you don't have to add them by hand. -Run `scanopy-daemon install --name ` for each instance. Each one registers its own background service — named `scanopy-daemon-` — and writes its namespaced config, so it starts immediately and on every reboot: +Run each instance's artifact on the host, **appending `--name `** to the `scanopy-daemon install` command: -```bash -sudo scanopy-daemon install --name eth0 --interfaces eth0 \ - --bind-address 192.168.1.10 --daemon-port 60073 \ - --server-url https://scanopy.example.com --daemon-api-key -sudo scanopy-daemon install --name iot --interfaces eth1 \ - --bind-address 10.0.0.10 --daemon-port 60074 \ - --server-url https://scanopy.example.com --daemon-api-key -``` + +The install command the app generates doesn't include `--name` — a daemon normally learns its name from the server. On a multi-instance host you have to add it yourself. Without it, every instance installs over the same service and the same config file, and only the last one survives. + + +Give each instance the name you used for its daemon entry in the app. `install` then registers a per-instance background service — named `scanopy-daemon-` — and writes a namespaced config, so each starts immediately and on every reboot. Manage each instance by its service name — for example `sudo systemctl restart scanopy-daemon-eth0` (Linux), `sudo launchctl kickstart -k system/com.scanopy.daemon.eth0` (macOS), or `sc.exe stop scanopy-daemon-iot` (Windows). The bind address and port are persisted in each daemon's config file, so re-running `install` or restarting the service doesn't need them again. diff --git a/docs/content/docs/reference/daemon-configuration.mdx b/docs/content/docs/reference/daemon-configuration.mdx index fc945fe..4991b5d 100644 --- a/docs/content/docs/reference/daemon-configuration.mdx +++ b/docs/content/docs/reference/daemon-configuration.mdx @@ -40,12 +40,16 @@ environment: **Configuration file**: -The daemon automatically creates a config file at: +A daemon you run yourself creates its config file under your user profile: - **Linux**: `~/.config/scanopy/daemon/config.json` - **macOS**: `~/Library/Application Support/com.scanopy.daemon/config.json` - **Windows**: `%APPDATA%\scanopy\daemon\config.json` +A daemon installed as a service uses a system directory instead. A background service runs under a different profile than whoever installed it, so `install` writes the config to a system location and points the service at it explicitly. The install command prints the config and log paths it settled on — read them off the output rather than guessing. + +Use `--config-dir` / `SCANOPY_CONFIG_DIR` to override the directory holding `config.json`. + The config file stores runtime state (daemon ID, host ID) alongside your settings. Command-line and environment variables take priority over the file. ## Parameter Reference @@ -86,68 +90,17 @@ Seeded credentials are applied **only at first registration**. If the daemon is There is no flag for scanning the local Docker or Podman socket. Local sockets are ordinary credentials — create a [Docker Socket](/guides/integrations/docker/) or [Podman Socket](/guides/integrations/podman/) credential and, if seeding it here, reference it with the loopback form above. -## Deprecated in v0.15.0 - - -The following parameters are deprecated and will be removed in v0.16.0. Use [Docker Proxy credentials](/guides/integrations/docker/) instead. See the [migration guide](/guides/operations/unified-discovery-migration/#1-recreate-docker-proxy-configuration-as-a-credential) for steps. - - -- `--docker-proxy` / `SCANOPY_DOCKER_PROXY` -- `--docker-proxy-ssl-cert` / `SCANOPY_DOCKER_PROXY_SSL_CERT` -- `--docker-proxy-ssl-key` / `SCANOPY_DOCKER_PROXY_SSL_KEY` -- `--docker-proxy-ssl-chain` / `SCANOPY_DOCKER_PROXY_SSL_CHAIN` - -Scan speed settings (`--scan-rate-pps`, `--arp-rate-pps`, etc.) configured at the daemon level are no longer used. Scan speed is now configured per-discovery in the **Speed** tab. See the [migration guide](/guides/operations/unified-discovery-migration/#2-re-apply-scan-speed-overrides). - -## Concurrent Scans - -Controls how many hosts the daemon scans simultaneously during network discovery. - -**Default behavior**: Auto-detected based on system resources - -- Calculates based on available memory -- Typical range: 10-20 for most systems -- Adjusts to prevent memory exhaustion - -**When to set manually**: +## Deprecated Parameters -- System crashes during scans -- Memory errors in logs -- Very large networks (100+ hosts) -- Resource-constrained devices (Raspberry Pi) - -**Recommended values**: - -- **Raspberry Pi 4 (4GB)**: 5-10 -- **Standard desktop**: 15-20 -- **Server**: 20-30+ -- **Low memory**: Start with 5, increase gradually - -**Setting**: - -```bash -# CLI -scanopy-daemon --concurrent-scans 10 - -# Environment -export SCANOPY_CONCURRENT_SCANS=10 - -# Docker -environment: - - SCANOPY_CONCURRENT_SCANS=10 -``` +The Docker proxy parameters (`--docker-proxy`, `--docker-proxy-ssl-cert`, `--docker-proxy-ssl-key`, `--docker-proxy-ssl-chain`, and their `SCANOPY_*` equivalents) are superseded by [Docker Proxy credentials](/guides/integrations/docker/). See the [migration guide](/guides/operations/unified-discovery-migration/#1-recreate-docker-proxy-configuration-as-a-credential) for steps. -**Symptoms of too high**: +Scan speed settings (`--scan-rate-pps`, `--arp-rate-pps`, and similar) configured at the daemon level are no longer used. Scan speed is configured per-discovery in the **Speed** tab. See the [migration guide](/guides/operations/unified-discovery-migration/#2-re-apply-scan-speed-overrides). -- Daemon crashes during scans -- "CONCURRENT_SCANS too high for this system" error -- Out of memory errors -- System becomes unresponsive +## Scan Concurrency -**Impact**: +The daemon decides how many hosts to scan simultaneously on its own, based on available memory and its file descriptor limit. There is no setting to configure it. -- Lower value = slower scans, more stable -- Higher value = faster scans, more memory usage +If scans are slower than expected because concurrency has been clamped low, the usual cause is a restrictive file descriptor limit — see ["Too Many Open Files"](/setting-up-daemons/troubleshooting-scans/scan-performance/#too-many-open-files-error). ## Logging diff --git a/docs/content/docs/reference/daemon-identity.mdx b/docs/content/docs/reference/daemon-identity.mdx new file mode 100644 index 0000000..8ae3b75 --- /dev/null +++ b/docs/content/docs/reference/daemon-identity.mdx @@ -0,0 +1,84 @@ +--- +title: Daemon Identity +description: How daemons are provisioned, how API keys bind to them, and how legacy shared keys behave. +--- + +Every daemon has a server-side record and an API key bound to it. This page describes how that +binding is established and enforced. For the settings a daemon reads at runtime, see +[Daemon Configuration](/reference/daemon-configuration/). For creating and managing daemons, see +[Managing Daemons](/setting-up-daemons/managing-daemons/). + +## Provisioning + +Creating a daemon in **Discover > Scan > Daemons** provisions it: + +1. The server creates the daemon record. +2. The server mints an API key and binds it to that record. +3. The wizard emits an install artifact carrying the key. + +The key's plaintext is available only at the moment it is minted. It appears in the install +artifact the wizard shows you and is not retrievable afterwards. + +You do not create daemon API keys yourself. The **Platform > API Keys** daemon tab lists keys but +no longer creates them. + +## Key binding + +A provisioned key belongs to exactly one daemon, and a daemon has exactly one key. + +| Property | Behavior | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Binding** | One key, one daemon. Enforced by the server, not by convention. | +| **Re-provisioning** | Minting a new key for a daemon deletes the key it replaces. The old key stops authenticating immediately. | +| **Plaintext storage** | ServerPoll only. The server holds the plaintext because it presents the key when it dials the daemon. DaemonPoll daemons carry their own key and the server stores only a hash. | + +## How the server identifies a daemon + +| Key type | Identity comes from | `X-Daemon-ID` header | +| ----------------------- | ------------------- | ------------------------------------------------------------------------------------------------- | +| Bound (1:1) | The key itself | Optional. If present and it doesn't match the key's daemon, the request is rejected as key reuse. | +| Legacy (network-shared) | The header | Required. Without it the daemon is unidentifiable and the request is rejected. | + +## Version floor + +Server-provisioned identity requires **daemon version 0.17.5 or later**. A 0.17.5+ daemon starts +with no identity of its own and learns which daemon it is from the key during its first handshake +with the server. + +Daemons below 0.17.5 self-register against a network-shared key and identify themselves with the +`X-Daemon-ID` header. Both models are accepted concurrently — which one applies is determined by +the shape of the key presented, not by the daemon's version. + +## Legacy network-shared keys + +Keys minted before 1:1 provisioning are not bound to any daemon. A single such key is shared by +every daemon on its network. + +- They continue to authenticate. Nothing expires them. +- They cannot be created anymore. +- They are marked as legacy in **Platform > API Keys**. +- A daemon using one can be given a key of its own without losing its host, discovery jobs, or + history — see + [Binding a key to a legacy daemon](/setting-up-daemons/managing-daemons/#binding-a-key-to-a-legacy-daemon). + +## Fixed and editable properties + +Provisioning fixes the properties that define what a daemon _is_. The rest can be edited on the +daemon record afterwards. + +| Property | | +| -------------------- | ---------------------------------------------------------------------------------------- | +| Network | Fixed at provision time | +| Polling mode | Fixed at provision time | +| Name | Fixed at provision time | +| Host | Created at provision time | +| Key binding | Maintained by provisioning | +| Version | Self-reported by the daemon on each handshake | +| Liveness and standby | Server-managed | +| URL | Editable, ServerPoll only — a DaemonPoll daemon is never dialed, so it has no URL to set | +| Maintainer, tags | Editable | + + +A daemon's identity is tied to the machine it runs on, so there is no way to move one to a +different host. See [Can I migrate a daemon?](/setting-up-daemons/managing-daemons/#can-i-migrate-a-daemon). + diff --git a/docs/content/docs/reference/daemon-status.mdx b/docs/content/docs/reference/daemon-status.mdx index d36e5ce..b3a0732 100644 --- a/docs/content/docs/reference/daemon-status.mdx +++ b/docs/content/docs/reference/daemon-status.mdx @@ -3,18 +3,35 @@ title: Daemon Status description: What each daemon status tag means and how to resolve issues. --- -Scanopy monitors daemon health and displays status tags in the UI. A daemon with no issues shows no tag. When problems are detected, a colored tag appears next to the daemon name indicating the most important issue. +Scanopy monitors daemon health and displays a status tag next to each daemon name in the UI. Every daemon carries exactly one tag, showing the most important condition that currently applies. ## Status Reference | Status | Meaning | | ------ | ------- | | | Running current version, communicating normally | +| | Provisioned, but the daemon has never contacted the server | | | No discovery session in the last 30 days | | | Server cannot reach the daemon | | | Daemon version is no longer supported | | | Newer daemon version available | +## Awaiting Connection + +**Blue tag**. The daemon record exists but the daemon has never checked in. + +Every daemon passes through this state. Creating a daemon [provisions](/reference/daemon-identity/) its record on the server before the daemon itself exists, so the tag appears as soon as you finish the wizard and clears on first contact. + +**How to resolve**: + +1. Run the install artifact from the wizard on the daemon host — until you do, there is nothing to connect +2. Confirm the daemon process or service is running (`systemctl status scanopy-daemon`, `launchctl print system/com.scanopy.daemon`, `Get-Service scanopy-daemon`, or `service scanopy-daemon status`) +3. **DaemonPoll**: check the daemon can reach the server on port 60072 +4. **ServerPoll**: check the daemon's URL is correct and the server can reach it on port 60073 +5. Check the daemon logs for registration errors — `daemon_not_provisioned` means a 0.17.5+ daemon tried to register itself against a record that doesn't exist, which happens when it was installed by hand with a legacy shared key instead of from a provisioned daemon's install artifact + +If the tag persists, see [Daemon Troubleshooting](/setting-up-daemons/troubleshooting-setup). + ## Standby **Purple tag**. The daemon is registered but hasn't run a discovery session in over 30 days. @@ -56,13 +73,13 @@ See [Daemon Troubleshooting](/setting-up-daemons/troubleshooting-setup) for deta ## Healthy -No tag is shown. The daemon is running a current version and communicating with the server normally. +**Green tag**. The daemon is running a current version and communicating with the server normally. ## Status Priority -When multiple statuses apply, the UI shows the highest-priority one: +When multiple conditions apply, the UI shows the highest-priority one: -**Standby > Unreachable > Deprecated > Outdated** +**Unreachable > Standby > Awaiting Connection > Deprecated > Outdated > Healthy** The Home tab shows all active statuses across your daemons. diff --git a/docs/content/docs/reference/limitations.mdx b/docs/content/docs/reference/limitations.mdx index 1742068..a84e091 100644 --- a/docs/content/docs/reference/limitations.mdx +++ b/docs/content/docs/reference/limitations.mdx @@ -62,7 +62,7 @@ See [Service Detection](/reference/service-detection/) for how detection works a ## SNMP -- **SNMPv3 is AuthPriv only** — authentication *and* privacy are required. AuthNoPriv and NoAuthNoPriv modes are not supported, and MD5 authentication is not available (use SHA-1 or SHA-256). SNMPv1 and SNMPv2c (community string) are also supported. +- **SNMPv3 is AuthPriv only** — authentication _and_ privacy are required. AuthNoPriv and NoAuthNoPriv modes are not supported, and MD5 authentication is not available (use SHA-1 or SHA-256). SNMPv1 and SNMPv2c (community string) are also supported. - **No trap/inform handling** — SNMP data is collected during discovery scans only (poll-based). Scanopy does not listen for SNMP traps or informs. - **No custom MIB loading** — only a fixed set of standard MIBs are queried. See [SNMP reference](/reference/snmp/#queried-mibs) for the full list. @@ -76,7 +76,7 @@ The Application perspective requires manual setup — you need to define applica ### Docker on macOS/Windows -Docker Desktop on macOS and Windows doesn't support host networking — the container can't see your actual network. Install the daemon as a native service instead (`scanopy-daemon install`, or the signed MSI on Windows). +Docker Desktop on macOS and Windows doesn't support host networking — the container can't see your actual network. Install the daemon as a native service instead (`scanopy-daemon install`). ## Requesting Features diff --git a/docs/content/docs/reference/meta.json b/docs/content/docs/reference/meta.json index 2f6a696..946687a 100644 --- a/docs/content/docs/reference/meta.json +++ b/docs/content/docs/reference/meta.json @@ -2,6 +2,7 @@ "title": "Reference", "defaultOpen": true, "pages": [ + "daemon-identity", "daemon-configuration", "daemon-status", "server-configuration", diff --git a/docs/content/docs/reference/snmp.mdx b/docs/content/docs/reference/snmp.mdx index 50cda08..830dff6 100644 --- a/docs/content/docs/reference/snmp.mdx +++ b/docs/content/docs/reference/snmp.mdx @@ -42,12 +42,17 @@ A successful SNMP run against a host populates the following entities in Scanopy | Parameter | Default | Description | | --------- | ------- | ----------- | -| **Request timeout** | 5s | Timeout for a single SNMP GET or GETNEXT request | +| **Probe timeout** | 2s | Liveness probe per credential, used to pick a working credential before collection | +| **Request timeout** | 5s | Timeout for a single SNMP request | | **Session timeout** | 5s | Timeout for establishing the UDP session | -| **Walk timeout** | 30s | Maximum time for a full SNMP walk of a MIB table | +| **Query timeout** | 30s | Ceiling on any single query, including a full walk of a MIB table | | **Max entries** | 10,000 | Maximum rows returned from a single table walk | | **Integration timeout** | 5m | Wall-clock budget for a full SNMP integration run against a single host (probe + all queries) | +Candidate credentials are probed concurrently, so a host with several assigned credentials doesn't pay for each one in sequence. + +Every query is individually bounded, so a device that stops responding mid-collection costs 30 seconds rather than the whole 5-minute integration budget. + ## Credential resolution order When querying a host, the daemon resolves the SNMP credential (community string for v1/v2c, or USM parameters for v3) in this order: @@ -57,3 +62,5 @@ When querying a host, the daemon resolves the SNMP credential (community string 3. **Fallback** — community string `public` (v2c) If all credentials fail on both UDP 161 and 1161, the host is discovered without SNMP-sourced data. Individual query failures within a successful session are logged and skipped — they do not abort the rest of the collection. + +Partial results are kept. Interfaces are saved as soon as the interface table is read, before the neighbor, forwarding-table, and VLAN queries run, so a device that hangs partway through still yields its interface list rather than nothing. diff --git a/docs/content/docs/self-hosted-server/troubleshooting.mdx b/docs/content/docs/self-hosted-server/troubleshooting.mdx index 53694bf..44c627c 100644 --- a/docs/content/docs/self-hosted-server/troubleshooting.mdx +++ b/docs/content/docs/self-hosted-server/troubleshooting.mdx @@ -71,7 +71,7 @@ docker exec scanopy-daemon curl http://scanopy-server:60072/api/health 2. **Update compose file**: If gateway isn't `172.17.0.1`, update `SCANOPY_INTEGRATED_DAEMON_URL` -3. **Check API key**: Ensure the integrated daemon has a valid API key in the database +3. **Check the daemon record**: Server setup [provisions](/reference/daemon-identity/) the integrated daemon and its API key. Confirm the daemon appears in **Discover > Scan > Daemons** and that its `daemon-config` volume still holds the key — a wiped volume leaves the daemon with no identity to present. ## Database Issues diff --git a/docs/content/docs/self-hosted-server/upgrading.mdx b/docs/content/docs/self-hosted-server/upgrading.mdx index 3b9b9f7..a3bdc8f 100644 --- a/docs/content/docs/self-hosted-server/upgrading.mdx +++ b/docs/content/docs/self-hosted-server/upgrading.mdx @@ -13,13 +13,19 @@ docker compose up -d ``` -**Podman users**: If you pull the daemon image by tag (e.g., `podman pull …/daemon:v0.15.2`), add `--platform linux/amd64` (or `linux/arm64`) to avoid pulling the wrong architecture. See [Troubleshooting — Podman Pulls Wrong Daemon Version](/setting-up-daemons/troubleshooting-setup/#podman-pulls-wrong-daemon-version-amd64). + **Podman users**: If you pull the daemon image by tag (e.g., `podman pull …/daemon:v0.15.2`), add + `--platform linux/amd64` (or `linux/arm64`) to avoid pulling the wrong architecture. See + [Troubleshooting — Podman Pulls Wrong Daemon + Version](/setting-up-daemons/troubleshooting-setup/#podman-pulls-wrong-daemon-version-amd64). This upgrades both the server and any daemons defined in your compose file. Volumes are preserved, so no re-registration or reconfiguration is needed. -If you use the integrated daemon, do not delete the `daemon-config` volume during upgrades — it stores the daemon's identity (ID, API key, and network assignment). Only delete it if you need to re-register the daemon from scratch. + If you use the integrated daemon, do not delete the `daemon-config` volume during upgrades — it + stores the daemon's identity (ID, API key, and network assignment). That identity is [provisioned + once at server setup](/reference/daemon-identity/) and cannot be re-derived, so deleting the + volume means provisioning the daemon again from scratch. After restarting, the daemon reports its new version to the server and the version status should immediately show **Current** in the UI. diff --git a/docs/content/docs/setting-up-daemons/managing-daemons.mdx b/docs/content/docs/setting-up-daemons/managing-daemons.mdx index 84fc1ca..b54c35f 100644 --- a/docs/content/docs/setting-up-daemons/managing-daemons.mdx +++ b/docs/content/docs/setting-up-daemons/managing-daemons.mdx @@ -7,7 +7,7 @@ description: Create, upgrade, and remove daemons. - **Linux**: Docker with host networking OR standalone binary (installable as a systemd service) - **macOS**: Standalone binary — Docker Desktop doesn't allow access to host interfaces. Installs as a launchd service. -- **Windows**: Standalone binary — Docker Desktop doesn't allow access to host interfaces. Installs as a Windows service (native SCM), or via the signed MSI. +- **Windows**: Standalone binary — Docker Desktop doesn't allow access to host interfaces. Installs as a Windows service (native SCM). - **FreeBSD**: Standalone binary, installable as an rc.d service. On Linux, macOS, Windows, and FreeBSD the daemon installs as a native background service — see [Running as a Service](#running-as-a-service). @@ -18,7 +18,23 @@ Go to **Discover > Scan > Daemons** and click **Create Daemon**. The wizard walk 1. **Setup** — network, polling mode, and daemon name 2. **Integrations** — optionally add SNMP, Docker, and Podman credentials, targeting all hosts or specific hosts. These are [auto-assigned](/using-scanopy/credentials/#auto-assignment) to discovered hosts after the first scan. See [Credentials](/using-scanopy/credentials/#during-daemon-setup-new-daemons) for details. -3. **Install** — a ready-to-copy `scanopy-daemon install` command with the correct API key, server URL, daemon name, and any `--credential-id` flags +3. **Install** — pick your platform and install method; the wizard builds the matching artifact for you + +Finishing the wizard [provisions](/reference/daemon-identity/) the daemon: the server creates its record and mints an API key bound to it. You don't create a key yourself. + +Choose an install method on the final step: + +| Method | What you get | +| ------ | ------------ | +| **Linux / macOS / FreeBSD** | A command that fetches the daemon binary and installs it as a native service | +| **Windows** | A PowerShell command that downloads the binary and installs it as a Windows service | +| **Docker** | A ready-to-run `docker-compose.yml` plus the `SCANOPY_*` environment variables it's configured with | + + +The generated artifact carries the daemon's API key, and the key is shown only here. Copy it before leaving the wizard — you can't retrieve it afterwards. If you lose it before installing, you can mint a replacement from the daemon's management modal; once the daemon has connected, re-keying it is blocked, because a fresh key would cut off the running daemon. + + +Any [advanced settings](/reference/daemon-configuration/) you set in the wizard are baked into the artifact, so the installed daemon comes up already configured. For help choosing a polling mode and planning where to place daemons, see [Planning Daemon Deployment](/setting-up-daemons/planning-daemon-deployment/). @@ -26,13 +42,9 @@ For help choosing a polling mode and planning where to place daemons, see [Plann The recommended way to install the daemon is the **`scanopy-daemon install`** command. It places the binary, writes the daemon's `config.json`, and registers a native background service — **systemd** on Linux, **launchd** on macOS, the **Service Control Manager** on Windows, and **rc.d** on FreeBSD — that starts immediately and on every reboot. The install command shown on the wizard's **Install** step already includes this; copy and run it on the host. -You can also run it yourself with the connection flags (the same flags the daemon accepts): +`install` accepts the same connection and identity flags as the daemon itself, so you can also invoke it by hand. Get the invocation from the app rather than assembling it yourself — the wizard and the management modal build it against your server's own URL and the daemon's key. -```bash -sudo scanopy-daemon install --server-url https://scanopy.example.com --daemon-api-key -``` - -Everything else — network, interfaces, ports — is read from `config.json`, so no secrets appear in the service definition or process arguments. Re-running `install` is idempotent, and a non-elevated invocation prints the exact `sudo`/administrator command to re-run. +Everything else — network, interfaces, ports — is read from `config.json`, so no secrets appear in the service definition or process arguments. Re-running `install` is idempotent, and a non-elevated invocation prints the exact `sudo`/administrator command to re-run. On success it prints the config and log paths it settled on. Verify the service and view its logs: @@ -47,16 +59,6 @@ To stop and remove the service, use `sudo scanopy-daemon uninstall` — see [Rem Prefer to run the daemon in the foreground (e.g. for a quick test)? Run `scanopy-daemon` directly with the same flags — it runs attached to your terminal and stops when the terminal closes. Running multiple daemons on one host? See [Running Multiple Daemons](/guides/networking/multiple-daemons/). -### Windows MSI - -On Windows you can also install with the signed **MSI** — the platform-standard wizard. It installs the binary and registers the same service. For unattended deployment (GPO/Intune), install silently: - -```powershell -msiexec /qn /i scanopy-daemon-windows-amd64.msi SERVERURL="https://scanopy.example.com" APIKEY="" -``` - -The MSI also handles upgrades in place and can be removed with `msiexec /qn /x scanopy-daemon-windows-amd64.msi`. - ### Manually installed or older daemons Daemons that predate the `scanopy-daemon install` subcommand, or that you set up by hand, are managed with the platform's native service tools directly. Use these instructions if `scanopy-daemon install` isn't available in your daemon version. (For current versions, prefer `install`/`uninstall` above.) @@ -109,22 +111,43 @@ nssm start ScanopyDaemon ## Integrated Daemon (Self-Hosted) -Self-hosted Docker Compose deployments include an **integrated daemon** that runs alongside the server. This daemon is managed as part of your Compose stack — it upgrades when you upgrade the server and doesn't need separate installation or API key setup. For upgrading the integrated daemon along with your server, see [Upgrading a Self-Hosted Server](/self-hosted-server/upgrading/). +Self-hosted Docker Compose deployments include an **integrated daemon** that runs alongside the server. This daemon is managed as part of your Compose stack — it upgrades when you upgrade the server and needs no separate installation. Server setup provisions it and its API key for you, so there is nothing to configure by hand. For upgrading the integrated daemon along with your server, see [Upgrading a Self-Hosted Server](/self-hosted-server/upgrading/). ## Upgrading -When an update is available, the daemon card in **Discover > Scan > Daemons** shows an **Update** button. Click it to see platform-specific upgrade steps: download the latest binary (or run the latest MSI on Windows), then restart the service (`systemctl restart` / `launchctl kickstart -k` / `sc.exe stop` + `start` / `service … restart`). Configuration is preserved across upgrades. +When an update is available, the daemon card in **Discover > Scan > Daemons** shows an **Update** button. Click it to see platform-specific upgrade steps: download the latest binary, then restart the service (`systemctl restart` / `launchctl kickstart -k` / `sc.exe stop` + `start` / `service … restart`). Configuration is preserved across upgrades. For upgrading self-hosted server and integrated daemon together, see [Upgrading](/self-hosted-server/upgrading). ## Updating Daemon Properties -You can update daemon properties after creation by editing the [daemon config file](/reference/daemon-configuration). Restart the daemon's service for changes to take effect — `sudo systemctl restart scanopy-daemon` (Linux), `sudo launchctl kickstart -k system/com.scanopy.daemon` (macOS), `sc.exe stop scanopy-daemon` then `sc.exe start scanopy-daemon` (Windows), or `sudo service scanopy-daemon restart` (FreeBSD). For a foreground daemon, just restart the process. +Click the edit icon on a daemon card in **Discover > Scan > Daemons** to open its management modal. The modal has two tabs: **Details** for the daemon record, and **API Key** for its key. -The daemon's name is the only property that propagates to the server. All other config properties are local to the daemon. +On the **Details** tab you can edit the maintainer, tags, and — for ServerPoll daemons — the URL the server dials. Network, polling mode, and name are shown read-only: they're set when the daemon is [provisioned](/reference/daemon-identity/) and define what the daemon is. A DaemonPoll daemon has no editable URL, because the server never dials it. + +Daemon-local settings — interfaces, ports, log level, scan tuning — live in the [daemon config file](/reference/daemon-configuration/) on the host, not on the server record. Change them either by [reconfiguring the daemon](#reconfiguring-a-daemon) from the modal or by editing `config.json` directly. Editing the file requires a service restart to take effect — `sudo systemctl restart scanopy-daemon` (Linux), `sudo launchctl kickstart -k system/com.scanopy.daemon` (macOS), `sc.exe stop scanopy-daemon` then `sc.exe start scanopy-daemon` (Windows), or `sudo service scanopy-daemon restart` (FreeBSD). For a foreground daemon, just restart the process. Each daemon also reports its **capabilities** to the server: whether it has Docker or Podman socket access (for container discovery) and which subnets it has network interfaces on (for default scan targets). To update capabilities after changing the host's network configuration or Docker access, run a SelfReport discovery. +## Reconfiguring a Daemon + +Changing a daemon's advanced settings in the management modal produces a **reconfigure** artifact — the way settings you change in the app reach a daemon that's already installed. + +A reconfigure differs from an install: it carries no API key and doesn't re-fetch the binary. It only re-asserts configuration, layering it over the `config.json` already on the host. For a Docker daemon you get just the environment variables that changed, to swap into your existing Compose file rather than replacing it — your own Compose customizations stay intact. + +Run the artifact on the daemon host, then restart the service. + +## Binding a Key to a Legacy Daemon + +Daemons set up before 1:1 provisioning authenticate with a key shared across every daemon on their network. They keep working indefinitely, but a shared key can't be rotated or revoked for one daemon without affecting the rest. Binding gives the daemon a key of its own, keeping its host, discovery jobs, and history. + +Open the daemon's management modal, go to the **API Key** tab, and click **Bind a Key**. What happens next depends on the polling mode: + +- **DaemonPoll** — the daemon keeps running on its shared key. You get a command to run on the daemon host to switch it over, and can do that whenever it suits you. +- **ServerPoll** — a hard cutover. The server starts using the new key to connect immediately, so the daemon stops responding until you reconfigure it. Only do this when you can get to the host right away. + +Scanopy asks you to confirm before a ServerPoll cutover. See [Daemon Identity](/reference/daemon-identity/) for how the two key models coexist. + ## Removing a Daemon To fully remove a daemon, delete it from the UI and uninstall it from the host. @@ -141,7 +164,9 @@ Deleting or uninstalling a daemon does not delete discovered data. Hosts, servic sudo scanopy-daemon uninstall ``` -This stops and deregisters the service and deletes its `config.json`. Add `--purge` to also delete the installed binary, and `--name ` to target a specific instance. On Windows, run it from an elevated PowerShell, or uninstall the MSI: `msiexec /qn /x scanopy-daemon-windows-amd64.msi`. +This stops and deregisters the service and deletes its `config.json`. Add `--purge` to also delete the installed binary and log files, and `--name ` to target a specific instance. On Windows, run it from an elevated PowerShell. + +`uninstall` reports what it did for every item it touched — removed, left in place, or not found — so you can see exactly what remains. It's idempotent: uninstalling a daemon that was never installed succeeds and tells you there was nothing to remove. Docker: diff --git a/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx b/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx index 3d834f9..43a938e 100644 --- a/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx +++ b/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx @@ -113,7 +113,7 @@ sudo scanopy-daemon install --name iot --interfaces eth1 \ Both `--bind-address` and `--daemon-port` are required when running multiple daemons on one host. Without them, the second instance will fail with a port binding error. See [Configuring ports and bind addresses](/guides/networking/multiple-daemons/#configuring-ports-and-bind-addresses) for details. -Create a separate daemon entry in **Discover > Daemons** for each instance. Each gets its own API key and can target the same or different Scanopy networks. +Create a separate daemon entry in **Discover > Daemons** for each instance. Each is provisioned with its own API key and can target the same or different Scanopy networks. This gives you Layer 2 discovery on each segment without needing a separate physical host per VLAN. See [Scanning Isolated Networks from One Host](/guides/networking/multiple-daemons/) for the full setup guide, including config namespacing, systemd templates, and Docker configuration. @@ -152,7 +152,7 @@ Each daemon uses a polling mode that determines which side initiates the connect |---|---|---| | **Connection direction** | Daemon → Server | Server → Daemon | | **Firewall requirements** | Daemon needs outbound access to server | Server needs inbound access to daemon (port 60073) | -| **Setup** | Daemon self-registers with an API key | Admin provisions the daemon in the UI and configures its URL | +| **Setup** | Create the daemon in the UI, then run its install artifact on the host | Same, plus a reachable URL for the server to dial | | **Best for** | Most deployments — works behind NAT and firewalls | DMZ or restricted environments where the daemon cannot make outbound connections | **Use DaemonPoll unless your daemon cannot reach the server.** It's simpler to set up and requires no inbound firewall rules on the daemon host. diff --git a/docs/content/docs/setting-up-daemons/troubleshooting-scans/scan-performance.mdx b/docs/content/docs/setting-up-daemons/troubleshooting-scans/scan-performance.mdx index 1786c3f..b26c90f 100644 --- a/docs/content/docs/setting-up-daemons/troubleshooting-scans/scan-performance.mdx +++ b/docs/content/docs/setting-up-daemons/troubleshooting-scans/scan-performance.mdx @@ -42,7 +42,7 @@ See [Daemon Configuration](/reference/daemon-configuration/) for the full parame - **Remove it** if you don't need visibility on that segment. - **Docker networks** (e.g. 172.17.0.0/16) should not be scanned over the network. Docker container discovery queries the Docker API directly and takes seconds. -**If the slow subnet is interfaced**: The daemon should already be using ARP. If the subnet is reasonably sized, check `concurrent_scans` — a very low value serializes work across many hosts. See [Concurrent Scans](/reference/daemon-configuration/#concurrent-scans). If the subnet is large (/16 or bigger), see [Large Interfaced Subnets](#large-interfaced-subnets) below. +**If the slow subnet is interfaced**: The daemon should already be using ARP. If the subnet is reasonably sized, check whether the daemon's scan concurrency has been clamped by a low file descriptor limit — see ["Too Many Open Files"](#too-many-open-files-error). If the subnet is large (/16 or bigger), see [Large Interfaced Subnets](#large-interfaced-subnets) below. ## Large Interfaced Subnets @@ -67,57 +67,47 @@ Each scan performs up to 3 ARP rounds (1 initial + 2 retries by default). Subseq Before increasing `arp_rate_pps`, check whether your switch has Dynamic ARP Inspection (DAI) enabled. High ARP rates can trigger rate limiting or port shutdown on managed switches. See [Switch Rate Limiting (DAI)](/setting-up-daemons/troubleshooting-scans/host-discovery/#switch-rate-limiting-dai). -## Discovery Fails with "CONCURRENT_SCANS too high" +## Discovery Finished with Hosts Unscanned -**Symptoms**: Daemon crashes or runs out of memory during scans +**Symptoms**: A run completes with a warning like `120 hosts not scanned (~35m remaining)`, and those hosts show stale data. -**Solution**: Reduce concurrent scans in daemon configuration: +**Cause**: The run hit **Max Discovery Duration** — the hard ceiling on a single discovery run, 6 hours by default. Scanopy completes the run rather than failing it, keeps what it collected, and leaves the rest for the next run. -**Docker:** - -```yaml -environment: - - SCANOPY_CONCURRENT_SCANS=10 # Reduce from default -``` - -**Binary:** +**Solutions**: -```bash -scanopy-daemon --concurrent-scans 10 ... -``` +- **Fix the underlying slowness first**. Hitting a 6-hour ceiling usually means something above is wrong — non-interfaced subnets being TCP-probed, oversized targets, or deep scans on a large host count. Work through [Discovery Takes Hours](#discovery-takes-hours) before raising the limit. +- **Narrow the targets** so a run covers less ground, or **add a daemon** on the slow segment. +- **Raise the limit** in the discovery's **Detection** tab if your network genuinely needs longer than the default window. -See [Daemon Configuration](/reference/daemon-configuration/#concurrent-scans) for recommended values. +If the warning appears on every run, the scan is not keeping up with its schedule and each run is starting from a further-behind position. ## "Too Many Open Files" Error -**Symptoms**: `Critical error scanning: Too many open files (os error 24)` in daemon logs +**Symptoms**: `Critical error scanning: Too many open files (os error 24)` or `CONCURRENT_SCANS is likely too high for this system` in daemon logs, or scans that are far slower than expected because only one host is scanned at a time. -**Causes**: System file descriptor limit is too low for the configured concurrent scans. +**Cause**: The daemon's file descriptor limit is too low. Deep scanning a single host needs a few hundred descriptors, so a low limit forces concurrency down — sometimes to one host at a time. -**Solutions**: - -1. **Reduce concurrent scans** (easiest): +On Unix, the daemon raises its own soft limit at startup (up to the hard limit, capped at 10,240), which handles the common case of macOS defaulting to 256. If you still see this, the **hard** limit is what needs raising. - ```yaml - environment: - - SCANOPY_CONCURRENT_SCANS=10 - ``` +**Solutions**: -2. **Increase system file descriptor limit**: +1. **Check what the daemon actually has**: ```bash - # Check current limit - ulimit -n + ulimit -Hn # hard limit — the ceiling the daemon can raise itself to + ulimit -n # current soft limit + ``` - # Increase temporarily - ulimit -n 65535 +2. **Raise the hard limit** (add to `/etc/security/limits.conf`, then log out and back in): - # Increase permanently (add to /etc/security/limits.conf) + ``` * soft nofile 65535 * hard nofile 65535 ``` -3. **For Docker**: Add to your daemon container: + On macOS, raise it with `launchctl limit maxfiles`. + +3. **For Docker**, set it on the daemon container: ```yaml ulimits: nofile: diff --git a/docs/content/docs/setting-up-daemons/troubleshooting-scans/snmp.mdx b/docs/content/docs/setting-up-daemons/troubleshooting-scans/snmp.mdx index c868264..73ab2d6 100644 --- a/docs/content/docs/setting-up-daemons/troubleshooting-scans/snmp.mdx +++ b/docs/content/docs/setting-up-daemons/troubleshooting-scans/snmp.mdx @@ -56,6 +56,22 @@ For SNMP-specific log messages, enable debug logging and look for these messages | `SNMP system info retrieved` with sys_descr/sys_name | Success | | `No working SNMP credential found for {ip}` | All credentials tried, none worked | +## Interfaces missing or incomplete + +**Symptoms**: A device's **ifEntry** tab shows fewer interfaces than the device has, or interfaces appear without their neighbor, VLAN, or learned-MAC data. + +**Cause**: The interface table walk was cut short. Scanopy caps a single table walk at 10,000 rows and bounds each query at 30 seconds, so a very large table — or a device that stalls partway through returning one — yields a partial result. + +Truncation is reported as a warning on the discovery session itself, so check the run in **Discover > Scan > Sessions** rather than only the daemon logs. + +Interfaces are saved as soon as the interface table is read, before the neighbor and VLAN queries run. So a device that hangs later in collection keeps its interface list but loses the enrichment — bare interfaces with no LLDP/CDP neighbor are the signature of that. + +**What to check**: + +- Whether the device responds to a full `snmpwalk` of the interface table from the daemon host, and how long it takes +- Whether the device is under load or rate-limiting SNMP +- Whether the interface count is genuinely near the 10,000-row cap (stacked chassis and large modular switches can be) + ## Check host SNMP data Open the host edit modal to inspect what SNMP has collected: diff --git a/docs/content/docs/setting-up-daemons/troubleshooting-setup.mdx b/docs/content/docs/setting-up-daemons/troubleshooting-setup.mdx index 56538cc..fb76693 100644 --- a/docs/content/docs/setting-up-daemons/troubleshooting-setup.mdx +++ b/docs/content/docs/setting-up-daemons/troubleshooting-setup.mdx @@ -71,7 +71,7 @@ Test-NetConnection -ComputerName your-server-hostname -Port 443 **Solutions**: 1. **Verify server URL**: Ensure `SCANOPY_SERVER_URL` is correct and reachable from the daemon host -2. **Check API key**: Verify the API key is valid and not expired in **Platform > API Keys** +2. **Check the API key**: Confirm the daemon's `config.json` holds the key from its install artifact. A daemon rejected with `daemon_not_provisioned` was installed by hand against a record that doesn't exist — create it in the UI and use the artifact that produces. See [Daemon Identity](/reference/daemon-identity/). 3. **Firewall rules**: Ensure outbound HTTPS (port 443) is allowed from the daemon host. On Windows, verify the daemon executable is allowed through **Windows Defender Firewall > Allow an app through firewall**. ## Permission Denied Errors (Linux) @@ -110,7 +110,8 @@ podman pull --platform linux/amd64 ghcr.io/scanopy/scanopy/daemon:v0.15.2 For ARM hosts, use `--platform linux/arm64` instead. -This only affects the daemon image. The server image uses explicit manifest creation and is not affected. + This only affects the daemon image. The server image uses explicit manifest creation and is not + affected. ## Getting Help diff --git a/docs/content/docs/using-scanopy/discovery.mdx b/docs/content/docs/using-scanopy/discovery.mdx index f5ad220..9cde95b 100644 --- a/docs/content/docs/using-scanopy/discovery.mdx +++ b/docs/content/docs/using-scanopy/discovery.mdx @@ -138,6 +138,14 @@ Factors affecting speed: If scan time is longer than expected, see [discovery takes hours](/setting-up-daemons/troubleshooting-scans/scan-performance/#discovery-takes-hours). +### Time limit + +A single discovery run has a hard ceiling, set by **Max Discovery Duration** in the discovery's **Detection** settings. The default is 21,600 seconds (6 hours). + +When a run hits the ceiling it completes rather than failing. Hosts still queued are left unscanned and picked up by the next run, and the finished run carries a warning naming how many hosts were skipped and roughly how much time they needed. Data already collected is kept. + +Raise the limit for large networks that legitimately need a longer window. If a run is hitting the ceiling unexpectedly, the scan is slower than it should be — see [discovery finished with hosts unscanned](/setting-up-daemons/troubleshooting-scans/scan-performance/#discovery-finished-with-hosts-unscanned). + ## Host Naming When a host is discovered, Scanopy determines its name using this priority: diff --git a/docs/content/docs/using-scanopy/organization.mdx b/docs/content/docs/using-scanopy/organization.mdx index 134e6e5..9eaace2 100644 --- a/docs/content/docs/using-scanopy/organization.mdx +++ b/docs/content/docs/using-scanopy/organization.mdx @@ -44,7 +44,11 @@ Manage credentials via **Assets > Credentials**. See [Credentials](/using-scanop ## API Keys -Each daemon requires its own API key. Create keys via **Platform > API Keys** or during daemon setup. Use one key per daemon — don't share keys between daemons. +Each daemon gets its own API key, minted and bound to it automatically when you create the daemon. You don't create daemon keys by hand. + +**Platform > API Keys** lists them. Any key marked legacy predates 1:1 binding and is shared across a network's daemons; see [Daemon Identity](/reference/daemon-identity/) for how those behave and how to give a daemon a key of its own. + +User API keys, for programmatic access to the API, are created here too — see the [API reference](/api/). ## Tags diff --git a/docs/src/components/status-tag.tsx b/docs/src/components/status-tag.tsx index 3feb762..f7991b1 100644 --- a/docs/src/components/status-tag.tsx +++ b/docs/src/components/status-tag.tsx @@ -1,4 +1,5 @@ const statusStyles: Record = { + 'Awaiting Connection': { bg: 'rgba(59, 130, 246, 0.15)', text: '#3b82f6' }, Standby: { bg: 'rgba(168, 85, 247, 0.15)', text: '#a855f7' }, Unreachable: { bg: 'rgba(239, 68, 68, 0.15)', text: '#ef4444' }, Deprecated: { bg: 'rgba(249, 115, 22, 0.15)', text: '#f97316' }, diff --git a/src/lib/fixtures/daemon-config.json b/src/lib/fixtures/daemon-config.json index 69ccb11..c73de4b 100644 --- a/src/lib/fixtures/daemon-config.json +++ b/src/lib/fixtures/daemon-config.json @@ -138,5 +138,15 @@ "default": "30", "description": "Seconds between heartbeat updates to the server", "docsOnly": false + }, + { + "id": "config_dir", + "label": "Config Directory", + "cliFlag": "--config-dir", + "envVar": "SCANOPY_CONFIG_DIR", + "configFileKey": "config_dir", + "default": null, + "description": "Directory holding this daemon's config.json. Defaults to the per-user platform location; installing as a service sets it to a system directory.", + "docsOnly": false } ] From 2272b19534172b58f39a9f55a49ed0f2b89344f7 Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 23 Jul 2026 10:30:44 -0400 Subject: [PATCH 4/5] Document daemon slots, staleness, container topology, and VIP dedup Sync docs with today's scanopy changes: - Rewrite the multiple-daemons guide around install slots (--name is gone); update managing/planning-daemon pages for the new selectors and auto-port - Add a Staleness concept page plus the per-network window setting, list badges, topology filter, and the 28-day default fact - Document container topology in L3 Logical (multi-attach, Same Container and Container Runtime overlays) and the By staleness metadata filter - Clarify host-targeted credentials assign only after a successful probe - Note CARP/VRRP floating-VIP deduplication under host matching --- .../guides/networking/multiple-daemons.mdx | 104 +++++++++--------- docs/content/docs/reference/faq.mdx | 4 + .../setting-up-daemons/managing-daemons.mdx | 6 +- .../planning-daemon-deployment.mdx | 12 +- .../troubleshooting-scans/credentials.mdx | 2 +- .../docs/using-scanopy/credentials.mdx | 2 +- docs/content/docs/using-scanopy/discovery.mdx | 2 + docs/content/docs/using-scanopy/meta.json | 2 +- .../docs/using-scanopy/network-data.mdx | 6 + .../docs/using-scanopy/organization.mdx | 4 + docs/content/docs/using-scanopy/staleness.mdx | 30 +++++ .../topology/customizing-views.mdx | 8 ++ .../using-scanopy/topology/l3-logical.mdx | 11 ++ 13 files changed, 127 insertions(+), 66 deletions(-) create mode 100644 docs/content/docs/using-scanopy/staleness.mdx diff --git a/docs/content/docs/guides/networking/multiple-daemons.mdx b/docs/content/docs/guides/networking/multiple-daemons.mdx index a9e3e52..3aad8ec 100644 --- a/docs/content/docs/guides/networking/multiple-daemons.mdx +++ b/docs/content/docs/guides/networking/multiple-daemons.mdx @@ -5,25 +5,30 @@ description: How to run multiple Scanopy daemon instances on a single machine, e This guide walks through running multiple daemon instances on a single host, each scanning different interfaces or network segments. For help deciding whether this is the right approach for your network, see [Planning Daemon Deployment](/setting-up-daemons/planning-daemon-deployment/). -## How config namespacing works +## How multiple daemons coexist -Every daemon instance has a name. The default is `scanopy-daemon`. When you pass `--name`, the daemon stores its configuration in a subdirectory named after that value: +Each `scanopy-daemon install` on a host takes its own **slot** — an independent install with its own config file, service, and log file. The first install takes the default slot (`scanopy-daemon`); each additional install takes the next number (`scanopy-daemon-2`, `scanopy-daemon-3`, …). The installer allocates the slot for you — you don't name it. -| Name | Config path (Linux) | -| --- | --- | -| _(default)_ | `~/.config/scanopy/daemon/config.json` | -| `--name eth0` | `~/.config/scanopy/daemon/eth0/config.json` | -| `--name iot` | `~/.config/scanopy/daemon/iot/config.json` | +| Slot | Service | Config (Linux) | +| --- | --- | --- | +| `scanopy-daemon` | `scanopy-daemon` | `/etc/scanopy/daemon/scanopy-daemon/config.json` | +| `scanopy-daemon-2` | `scanopy-daemon-2` | `/etc/scanopy/daemon/scanopy-daemon-2/config.json` | +| `scanopy-daemon-3` | `scanopy-daemon-3` | `/etc/scanopy/daemon/scanopy-daemon-3/config.json` | + +Each slot's config holds its own daemon ID, API key, server URL, and runtime state. Slots are fully independent — starting, stopping, or reconfiguring one does not affect the others. A slot is not the daemon's *name*: the name is assigned by the server and shown in the app, and can be changed there without moving the slot. -Each named daemon gets its own config file with its own daemon ID, API key, server URL, and runtime state. They are fully independent — starting, stopping, or reconfiguring one does not affect the others. + +A daemon's display name comes from the server, so you never pass it at install time. Run `scanopy-daemon list` on the host to see each installed slot, its service, and its config path. + -**Platform paths:** +**System config locations by platform** (service installs): | Platform | Base directory | | --- | --- | -| Linux | `~/.config/scanopy/daemon/` | -| macOS | `~/Library/Application Support/com.scanopy.daemon/` | -| Windows | `%APPDATA%\scanopy\daemon\` | +| Linux | `/etc/scanopy/daemon/` | +| macOS | `/Library/Application Support/Scanopy/daemon/` | +| Windows | `%ProgramData%\Scanopy\daemon\` | +| FreeBSD | `/usr/local/etc/scanopy/daemon/` | ## Restricting daemons to specific interfaces @@ -31,10 +36,10 @@ Use `--interfaces` to limit which network interfaces a daemon scans. Without it, ```bash # Scan only eth0 -sudo scanopy-daemon install --name eth0 --interfaces eth0 +sudo scanopy-daemon install --interfaces eth0 # Scan two interfaces -sudo scanopy-daemon install --name production --interfaces eth0,eth1 +sudo scanopy-daemon install --interfaces eth0,eth1 ``` The same setting works as an environment variable or in the config file: @@ -49,20 +54,16 @@ SCANOPY_INTERFACES=eth0,eth1 When `--interfaces` is set, the daemon only reports and scans subnets attached to those interfaces. This prevents overlap between daemon instances sharing the same host. -## Configuring ports and bind addresses +## Ports and bind addresses -Every daemon instance runs an HTTP server that binds to `0.0.0.0:60073` by default. Because `0.0.0.0` binds all interfaces, the first daemon claims port 60073 on every address — subsequent instances on the same host will fail with a port conflict. +Every daemon instance runs an HTTP server, bound to `0.0.0.0:60073` by default. Because `0.0.0.0` binds all interfaces, the first daemon claims port 60073 on every address — a second daemon can't reuse it. -To fix this, give each instance a unique `--bind-address` (the IP of its specific interface) and a unique `--daemon-port`: +The installer handles this for you: when you install another daemon and don't ask for a specific port, it picks the next free one (60074, 60075, …) so the new instance doesn't collide with one already on the host. You only need to set `--daemon-port` when you want a *specific* port — most importantly for a **ServerPoll** daemon, where the port is the one the server dials, so the install command already carries it and the installer leaves it alone. -```bash -# Daemon on eth0 (192.168.1.10) — uses default port -sudo scanopy-daemon install --name eth0 --interfaces eth0 \ - --bind-address 192.168.1.10 --daemon-port 60073 +To pin a daemon's listener to one interface, give it that interface's IP with `--bind-address`: -# Daemon on eth1 (10.0.0.10) — different port -sudo scanopy-daemon install --name eth1 --interfaces eth1 \ - --bind-address 10.0.0.10 --daemon-port 60074 +```bash +sudo scanopy-daemon install --interfaces eth0 --bind-address 192.168.1.10 ``` The same settings work as environment variables or in the config file: @@ -76,28 +77,30 @@ SCANOPY_DAEMON_PORT=60073 { "bind_address": "192.168.1.10", "daemon_port": 60073 } ``` - - Both `--bind-address` and `--daemon-port` are required when running multiple daemons on one host. Even if each daemon uses a different bind address, using the same port on `0.0.0.0` will cause a conflict. - - ## Setting up multiple daemons Create a separate daemon entry in **Discover > Scan > Daemons** for each instance. Each is provisioned with its own API key and can target the same or different networks. During setup, use the **Advanced** tab to set that instance's interface restrictions, bind address, and port under Network Discovery — they're baked into the install artifact the wizard gives you, so you don't have to add them by hand. -Run each instance's artifact on the host, **appending `--name `** to the `scanopy-daemon install` command: +Run each daemon's own install artifact on the host. Each run takes a fresh slot, so you can install several without them overwriting each other — no per-instance flags to remember: - -The install command the app generates doesn't include `--name` — a daemon normally learns its name from the server. On a multi-instance host you have to add it yourself. Without it, every instance installs over the same service and the same config file, and only the last one survives. - +```bash +# First daemon — takes the scanopy-daemon slot +sudo scanopy-daemon install --interfaces eth0 + +# Second daemon — takes the scanopy-daemon-2 slot automatically +sudo scanopy-daemon install --interfaces eth1 +``` -Give each instance the name you used for its daemon entry in the app. `install` then registers a per-instance background service — named `scanopy-daemon-` — and writes a namespaced config, so each starts immediately and on every reboot. +Each `install` registers a per-slot background service that starts immediately and on every reboot, and writes that slot's config. Manage each by its service — for example `sudo systemctl restart scanopy-daemon-2` (Linux) or `sc.exe stop scanopy-daemon-2` (Windows), where the service id matches the slot. On macOS the launchd label is `com.scanopy.daemon.` (for example `sudo launchctl kickstart -k system/com.scanopy.daemon.scanopy-daemon-2`). Run `scanopy-daemon list` to see each slot's exact service id. Interface, bind address, and port are persisted in each slot's config, so restarting or re-running `install` doesn't need them again. -Manage each instance by its service name — for example `sudo systemctl restart scanopy-daemon-eth0` (Linux), `sudo launchctl kickstart -k system/com.scanopy.daemon.eth0` (macOS), or `sc.exe stop scanopy-daemon-iot` (Windows). The bind address and port are persisted in each daemon's config file, so re-running `install` or restarting the service doesn't need them again. + +To re-run against an *existing* slot — reconfigure it, or re-key it after rebuilding the host — pass `--instance `, where the selector is the daemon's name, its slot, its service id, or its daemon id (all shown by `scanopy-daemon list`). Without a selector on a multi-daemon host, a non-interactive install allocates a new slot rather than guessing which one you meant, and an interactive one asks. The reconfigure command the app generates already includes the right `--instance`. + ### systemd template (Linux, manual) -Prefer `scanopy-daemon install --name ` above — it registers a per-instance service for you. The systemd template below is the manual alternative, kept for older daemons or hand-managed setups. +Prefer `scanopy-daemon install` above — it registers a per-slot service for you. The systemd template below is the manual alternative, kept for older daemons or hand-managed setups. Scanopy provides a [systemd template unit](https://github.com/scanopy/scanopy/blob/main/scanopy-daemon%40.service) for running multiple instances. Download and install it: @@ -108,10 +111,10 @@ sudo curl -o /etc/systemd/system/scanopy-daemon@.service \ sudo systemctl daemon-reload ``` -The template uses the instance identifier as both `--name` and `--interfaces`. Each instance needs its own bind address and port — set these via environment overrides: +The template uses the instance identifier as `--interfaces`. Each instance needs its own bind address and port — set these via environment overrides: ```ini -ExecStart=/usr/local/bin/scanopy-daemon --name=%i --interfaces=%i +ExecStart=/usr/local/bin/scanopy-daemon --interfaces=%i SyslogIdentifier=scanopy-daemon-%i ``` @@ -152,41 +155,34 @@ journalctl -u scanopy-daemon@eth1 ``` - The template ties the instance name to a single interface. If a daemon needs multiple interfaces, create a custom service file that passes `--interfaces eth0,eth1` and a descriptive `--name`. + The template ties the instance name to a single interface. If a daemon needs multiple interfaces, create a custom service file that passes `--interfaces eth0,eth1`. -## Listing existing daemon configs +## Listing installed daemons -To see which named daemons are configured on a host, list the config directory: +To see which daemons are installed on a host — their slots, services, config paths, and IDs — run: ```bash -# Linux -ls ~/.config/scanopy/daemon/ - -# macOS -ls ~/Library/Application\ Support/com.scanopy.daemon/ - -# Windows -dir %APPDATA%\scanopy\daemon\ +scanopy-daemon list ``` -Each subdirectory corresponds to a named daemon instance. A `config.json` at the top level is the default (unnamed) daemon. +Each line is one installed slot. Use any of the values it prints as the `--instance` selector for `install` or the `--name` selector for `uninstall`. ## Upgrading and restarting -All daemon instances share the same binary. Download the latest binary once (the app's **Update** button provides the command), then restart each instance's service: +All daemon slots share the same binary. Download the latest binary once (the app's **Update** button provides the command), then restart each slot's service: ```bash -# Instances installed with `scanopy-daemon install` (services named scanopy-daemon-) -sudo systemctl restart scanopy-daemon-eth0 -sudo systemctl restart scanopy-daemon-iot +# Slots installed with `scanopy-daemon install` +sudo systemctl restart scanopy-daemon +sudo systemctl restart scanopy-daemon-2 # Legacy systemd-template instances (scanopy-daemon@): stop, replace the binary, start sudo systemctl stop scanopy-daemon@eth0 scanopy-daemon@eth1 sudo systemctl start scanopy-daemon@eth0 scanopy-daemon@eth1 ``` -Configuration is preserved across upgrades — each daemon reads its settings from its own config file on startup. +Configuration is preserved across upgrades — each slot reads its settings from its own config file on startup. ## Docker diff --git a/docs/content/docs/reference/faq.mdx b/docs/content/docs/reference/faq.mdx index 0250cae..71907e2 100644 --- a/docs/content/docs/reference/faq.mdx +++ b/docs/content/docs/reference/faq.mdx @@ -72,6 +72,10 @@ Yes, but you'll miss some data. See [Layer 2 vs Layer 3](/reference/limitations/ For best results, deploy a daemon on each network segment. See [Planning Daemon Deployment](/setting-up-daemons/planning-daemon-deployment/). +### When does a host show as stale? + +When discovery hasn't observed it within its network's staleness window — **28 days** by default, adjustable per network. See [Staleness](/using-scanopy/staleness/). + ## Networking ### What ports does Scanopy use? diff --git a/docs/content/docs/setting-up-daemons/managing-daemons.mdx b/docs/content/docs/setting-up-daemons/managing-daemons.mdx index b54c35f..07bb0b1 100644 --- a/docs/content/docs/setting-up-daemons/managing-daemons.mdx +++ b/docs/content/docs/setting-up-daemons/managing-daemons.mdx @@ -56,9 +56,11 @@ Verify the service and view its logs: To stop and remove the service, use `sudo scanopy-daemon uninstall` — see [Removing a Daemon](#removing-a-daemon). -Prefer to run the daemon in the foreground (e.g. for a quick test)? Run `scanopy-daemon` directly with the same flags — it runs attached to your terminal and stops when the terminal closes. Running multiple daemons on one host? See [Running Multiple Daemons](/guides/networking/multiple-daemons/). +Prefer to run the daemon in the foreground (e.g. for a quick test)? Run `scanopy-daemon` directly with the same flags — it runs attached to your terminal and stops when the terminal closes. +Running several daemons on one host? Each `install` takes its own numbered **slot** (`scanopy-daemon`, `scanopy-daemon-2`, …) automatically, so installs don't overwrite each other. Run `scanopy-daemon list` to see what's installed. See [Scanning Isolated Networks from One Host](/guides/networking/multiple-daemons/) for the full workflow. + ### Manually installed or older daemons Daemons that predate the `scanopy-daemon install` subcommand, or that you set up by hand, are managed with the platform's native service tools directly. Use these instructions if `scanopy-daemon install` isn't available in your daemon version. (For current versions, prefer `install`/`uninstall` above.) @@ -164,7 +166,7 @@ Deleting or uninstalling a daemon does not delete discovered data. Hosts, servic sudo scanopy-daemon uninstall ``` -This stops and deregisters the service and deletes its `config.json`. Add `--purge` to also delete the installed binary and log files, and `--name ` to target a specific instance. On Windows, run it from an elevated PowerShell. +This stops and deregisters the service and deletes its `config.json`. Add `--purge` to also delete the installed binary and log files. On a host running several daemons, target one with `--name ` — the daemon's name, its slot, or its service id, all shown by `scanopy-daemon list` — or remove every one with `--all`. On Windows, run it from an elevated PowerShell. `uninstall` reports what it did for every item it touched — removed, left in place, or not found — so you can see exactly what remains. It's idempotent: uninstalling a daemon that was never installed succeeds and tells you there was nothing to remove. diff --git a/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx b/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx index 43a938e..3fc1310 100644 --- a/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx +++ b/docs/content/docs/setting-up-daemons/planning-daemon-deployment.mdx @@ -100,22 +100,20 @@ flowchart TB d2 -- "L2" --> vlan2["VLAN 2 - IoT
    192.168.2.0/24"] `} /> -Each instance gets a name and is restricted to specific interfaces so they don't overlap. Since every daemon binds an HTTP server, each instance also needs a unique `--bind-address` and `--daemon-port` to avoid port conflicts: +Restrict each instance to specific interfaces so they don't overlap. Each `install` takes its own slot automatically, so you don't pass any per-instance identity — and since every daemon binds an HTTP server, the installer picks a non-conflicting port for each after the first: ```bash -sudo scanopy-daemon install --name production --interfaces eth0 \ - --bind-address 192.168.1.10 --daemon-port 60073 -sudo scanopy-daemon install --name iot --interfaces eth1 \ - --bind-address 10.0.0.10 --daemon-port 60074 +sudo scanopy-daemon install --interfaces eth0 +sudo scanopy-daemon install --interfaces eth1 ``` - Both `--bind-address` and `--daemon-port` are required when running multiple daemons on one host. Without them, the second instance will fail with a port binding error. See [Configuring ports and bind addresses](/guides/networking/multiple-daemons/#configuring-ports-and-bind-addresses) for details. + Set `--bind-address` only to pin a daemon's listener to one interface, and `--daemon-port` only when you need a specific port (for a ServerPoll daemon it's already in the install command). See [Ports and bind addresses](/guides/networking/multiple-daemons/#ports-and-bind-addresses) for details. Create a separate daemon entry in **Discover > Daemons** for each instance. Each is provisioned with its own API key and can target the same or different Scanopy networks. -This gives you Layer 2 discovery on each segment without needing a separate physical host per VLAN. See [Scanning Isolated Networks from One Host](/guides/networking/multiple-daemons/) for the full setup guide, including config namespacing, systemd templates, and Docker configuration. +This gives you Layer 2 discovery on each segment without needing a separate physical host per VLAN. See [Scanning Isolated Networks from One Host](/guides/networking/multiple-daemons/) for the full setup guide, including how slots work, systemd templates, and Docker configuration. **Best for:** Hypervisors, routers, or multi-NIC servers bridging isolated VLANs where deploying separate hosts isn't practical. diff --git a/docs/content/docs/setting-up-daemons/troubleshooting-scans/credentials.mdx b/docs/content/docs/setting-up-daemons/troubleshooting-scans/credentials.mdx index 00a15ca..81cdd60 100644 --- a/docs/content/docs/setting-up-daemons/troubleshooting-scans/credentials.mdx +++ b/docs/content/docs/setting-up-daemons/troubleshooting-scans/credentials.mdx @@ -79,7 +79,7 @@ If a credential type shows **0 hosts**, the credential was loaded but never succ ## Credential loaded but not assigned to hosts -A credential can load from disk without errors but still show 0 hosts in the mapping summary. This happens when the actual authentication or connection attempt fails on the target device: +A credential can load from disk without errors but still show 0 hosts in the mapping summary. Right after a daemon is installed this is normal: a host-targeted credential is assigned to a host only once a scan probes it there successfully, so it stays unassigned until the first scan runs. If it's still unassigned after a scan, the authentication or connection attempt is failing on the target device: - **SNMP**: The credential was rejected by the device (wrong community string or SNMPv3 USM parameters, ACL blocking the daemon's IP, or that SNMP version disabled) - **Docker Proxy**: The TLS handshake failed (expired cert, CA mismatch), the proxy refused the connection, or the proxy is blocking required API endpoints diff --git a/docs/content/docs/using-scanopy/credentials.mdx b/docs/content/docs/using-scanopy/credentials.mdx index ff5f33f..5bbc817 100644 --- a/docs/content/docs/using-scanopy/credentials.mdx +++ b/docs/content/docs/using-scanopy/credentials.mdx @@ -36,7 +36,7 @@ When creating a new daemon, the **Integrations** step lets you add SNMP, Docker, - **Target All Hosts** — the credential becomes a network default, tried on every host the daemon scans. - **Target Specific Hosts** — the credential applies only to the hosts you name. Add the **daemon host** itself (its loopback address, `127.0.0.1` — used for local Docker and Podman sockets) or one or more **remote hosts** by IP. -After the daemon's first scan discovers hosts at those IPs, host-targeted credentials are [auto-assigned](/using-scanopy/credentials/#auto-assignment) to the matching hosts and used in subsequent scans without manual assignment. +Host-targeted credentials — including a daemon-host credential like a local Docker or Podman socket (`127.0.0.1`) — don't show as assigned the moment the daemon is installed. They're [auto-assigned](/using-scanopy/credentials/#auto-assignment) to a host only after the first scan probes them there successfully, and are then used in subsequent scans without manual assignment. Until that first scan runs, they're staged rather than assigned — that's expected, not a problem. ### From the discovery modal (existing daemons, new network segments) diff --git a/docs/content/docs/using-scanopy/discovery.mdx b/docs/content/docs/using-scanopy/discovery.mdx index 9cde95b..bc9566e 100644 --- a/docs/content/docs/using-scanopy/discovery.mdx +++ b/docs/content/docs/using-scanopy/discovery.mdx @@ -181,6 +181,8 @@ Scanopy primarily identifies duplicate hosts by comparing network interfaces. Tw MAC addresses are available when the daemon is directly connected to the subnet being scanned (via ARP) or from Docker container network configuration. See [Layer 2 vs Layer 3](/reference/limitations/#layer-2-vs-layer-3) for details on when MAC addresses are collected. +**Floating virtual IPs (CARP/VRRP).** A high-availability virtual IP has no hardware of its own — its only identity is the address it floats on, carried by a shared virtual-router MAC. Scanopy matches such an address on IP + subnet when it has no physical identity to match on, so a floating VIP is recognized as the same entity across scans instead of being recreated each time. The shared virtual MAC is never used to match, so the HA peers that host the VIP stay distinct rather than merging into one host. + ### Docker Host Matching Docker container discovery is local-only — it discovers containers on the daemon's own host (or on the host targeted by a Docker Proxy credential), not arbitrary remote machines. All discovered services are assigned to that host. diff --git a/docs/content/docs/using-scanopy/meta.json b/docs/content/docs/using-scanopy/meta.json index 1a3ac39..7ab61bc 100644 --- a/docs/content/docs/using-scanopy/meta.json +++ b/docs/content/docs/using-scanopy/meta.json @@ -1,5 +1,5 @@ { "title": "Using Scanopy", "defaultOpen": true, - "pages": ["discovery", "credentials", "topology", "organization", "network-data", "dependencies"] + "pages": ["discovery", "credentials", "topology", "organization", "network-data", "staleness", "dependencies"] } diff --git a/docs/content/docs/using-scanopy/network-data.mdx b/docs/content/docs/using-scanopy/network-data.mdx index 7621b04..26b066f 100644 --- a/docs/content/docs/using-scanopy/network-data.mdx +++ b/docs/content/docs/using-scanopy/network-data.mdx @@ -48,6 +48,10 @@ These relationships appear in the topology and host details, showing your virtua Scanopy automatically detects 200+ services. See [Service Detection](/reference/service-detection/) for how detection works, confidence levels, and what to do when a service isn't found. +### Stale Hosts + +Hosts discovery hasn't seen within their network's window carry a **Stale** badge, and the host list has a **Stale only** filter to isolate them. This flags devices that may have gone offline without deleting them. See [Staleness](/using-scanopy/staleness/) for how the window works and why an entity goes stale. + ### Hiding Hosts Mark hosts as hidden to flag them for exclusion from the topology. Hidden hosts remain in the database with all their services, interfaces, and discovery history intact. @@ -69,6 +73,8 @@ The button turns blue when a host is hidden. Use the **Hidden** column filter to Subnets represent network segments. Scanopy automatically detects subnets during discovery, but you can also create them manually. Assign a type (LAN, WiFi, DMZ, etc.) to label your subnets for filtering and organization. +Like hosts and services, subnets can go [stale](/using-scanopy/staleness/) — carrying a **Stale** badge and a **Stale only** filter when discovery hasn't seen them within their network's window. + ### Organizational Subnets Two special subnet types use CIDR `0.0.0.0/0` and serve as organizational containers rather than real network segments: diff --git a/docs/content/docs/using-scanopy/organization.mdx b/docs/content/docs/using-scanopy/organization.mdx index 9eaace2..40485cc 100644 --- a/docs/content/docs/using-scanopy/organization.mdx +++ b/docs/content/docs/using-scanopy/organization.mdx @@ -32,6 +32,10 @@ Networks are the primary organizational unit. Each network represents a distinct - One network per physical location - Distinct networks for different security zones +### Network Settings + +Open a network from **Assets > Networks** to edit its settings. Alongside its name and type, each network has a **Consider entities stale after** window — how long discovery can go without seeing a host, service, or subnet before it's flagged as [stale](/using-scanopy/staleness/). Enter it as days and hours, set it to comfortably exceed the network's scan interval, and leave it blank to use the default (28 days). + ### Network Access Users can be restricted to specific networks within an organization. When inviting a user, select which networks they may access — all data queries are filtered to those networks. This allows granting a contractor or external partner visibility into only the networks relevant to them while keeping other environments hidden. diff --git a/docs/content/docs/using-scanopy/staleness.mdx b/docs/content/docs/using-scanopy/staleness.mdx new file mode 100644 index 0000000..8946df9 --- /dev/null +++ b/docs/content/docs/using-scanopy/staleness.mdx @@ -0,0 +1,30 @@ +--- +title: Staleness +description: How Scanopy flags hosts, services, and subnets that discovery hasn't seen recently. +--- + +Scanopy marks an entity **Stale** when discovery hasn't observed it within its network's staleness window. Staleness is a signal that something may have gone quiet — a device powered off, a container removed, a host that moved — surfaced without deleting anything, so you decide what to do about it. + +Stale does **not** mean removed. A stale host might simply be switched off, on the road, or briefly unreachable during a scan. Scanopy never asserts that an entity is gone; it only reports how long it's been since discovery last saw it. + +## The staleness window + +Each network has its own window — how long an entity can go unobserved before it's flagged. Staleness is only meaningful relative to how often a network is scanned: a segment swept every 15 minutes and one swept monthly need very different thresholds, so the window lives on the network, not the whole organization. + +The default window is **28 days**. Set a network's own window in the network edit modal (**Assets > Networks** → select the network → **Consider entities stale after**), entered as days and hours. Set it to comfortably exceed the network's scan interval so a normal gap between scans never trips it. Leave it blank to use the default. + +See [Networks](/using-scanopy/organization/#networks) for where this fits among a network's settings. + +## What can go stale + +Only entities that discovery manages can go stale — hosts, services, and subnets that discovery found and keeps re-observing. Anything you created by hand never goes stale, because Scanopy has no scan timestamp to judge it against and won't flag your own curated records. + +Every entity is judged on **its own** last-seen time, independently of its parent. A service on a host that's gone quiet is only stale if that service itself hasn't been seen within the window — a recently observed service on an otherwise-dark host reads as current, and vice versa. This keeps a badge from ever contradicting its own "last seen" detail. + +## Where it shows up + +**Lists.** Hosts, services, and subnets each carry a **Stale** badge on rows past their window, and a **Stale only** filter to narrow the list to them. You can also sort by last-seen to bring the longest-unobserved entities to the top. + +**Topology.** Stale nodes carry a **Stale** tag on their card, with a "last seen" detail naming the entity and how long ago. A **By staleness** filter (Current / Stale) is available on the perspectives that show these entities, letting you fade everything except what's fallen behind. See [Customizing Views](/using-scanopy/topology/customizing-views/). + +The tag is **amber**, not red — matching the daemon status convention where red means broken and amber means behind. A stale entity may be perfectly healthy and simply unobserved, so the tag reads as "worth a look", not "error". diff --git a/docs/content/docs/using-scanopy/topology/customizing-views.mdx b/docs/content/docs/using-scanopy/topology/customizing-views.mdx index a454d7a..52808ad 100644 --- a/docs/content/docs/using-scanopy/topology/customizing-views.mdx +++ b/docs/content/docs/using-scanopy/topology/customizing-views.mdx @@ -33,6 +33,14 @@ Grouping rules change how things are organized in your topology. Use the **Group Each perspective shows different connection types. Use the edge filter controls to toggle connection types on or off. Some connections are shown by default and others are available as overlays — toggle them on when you need the extra detail. +Overlays available depend on the perspective. On L3 Logical, for example, **Same Container** links the addresses of a container attached to several bridge subnets (hidden by default), and **Container Runtime** connects each bridge subnet to its host. + +Clicking a connection highlights what it relates: for a relationship edge — such as Same Host or Same Container — the whole set of nodes it ties together lights up, while a point-to-point edge highlights only its own segment. + +## Metadata Filtering + +Beyond tags, some perspectives offer metadata filter groups that fade everything except the elements you're interested in. The **By staleness** group (Current / Stale) narrows the view to entities discovery hasn't seen within their network's window — see [Staleness](/using-scanopy/staleness/). Other groups include service category and, on Workloads, virtualization type. A filter group only appears when the entities in view actually differ on that dimension. + ## Tag-Based Filtering Filter your topology by tags to focus on what matters. Filtered elements fade to reduced opacity instead of disappearing, so the overall layout stays stable while your focus area stands out. diff --git a/docs/content/docs/using-scanopy/topology/l3-logical.mdx b/docs/content/docs/using-scanopy/topology/l3-logical.mdx index 70b8645..09bb3b5 100644 --- a/docs/content/docs/using-scanopy/topology/l3-logical.mdx +++ b/docs/content/docs/using-scanopy/topology/l3-logical.mdx @@ -17,6 +17,17 @@ This is the default perspective and the most general-purpose view of your networ The L3 view is built from your discovered subnets and the IP addresses found on each host's interfaces. No manual setup is required — it appears as soon as discovery runs. [Dependencies](/using-scanopy/dependencies) configured with port-level detail surface as overlay connections on top of the structural layout. +## Containers + +Container hosts (Docker, Podman) attach their containers to one or more bridge networks, and each bridge network is a subnet on this view. A container that joins several bridges appears in **each** of those subnets, so you can see everywhere it's attached rather than in just one place. + +Two overlays make container structure legible: + +- **Container Runtime** connects each bridge subnet back to its host, showing which host runs the containers on that bridge. +- **Same Container** links the addresses of a single container that spans multiple bridge subnets, so a multi-attached container reads as one thing rather than as unrelated cards in separate subnet boxes. It's hidden by default — toggle it on when you need it. + +The **Container Bridges** grouping rule collapses a host's bridge subnets into a single group when they'd otherwise clutter the view. See [Customizing Views](/using-scanopy/topology/customizing-views/) for grouping rules and edge overlays. + ## When to use it - General-purpose situational awareness of the network From 69a8eba4b60d9f76a0b50ca203014f18819c78c1 Mon Sep 17 00:00:00 2001 From: Maya Date: Thu, 23 Jul 2026 10:40:53 -0400 Subject: [PATCH 5/5] Fix all Dependabot/npm audit vulnerabilities in both lockfiles Root and docs now report 0 vulnerabilities; both build clean. - sharp: bump direct dep to ^0.35.3 (libvips CVEs, GHSA-f88m-g3jw-g9cj) - esbuild -> 0.28.1 (GHSA-g7r4-m6w7-qqqr): root via vite 7.3.6 which widens its range to ^0.28; docs via an override (fumadocs-mdx pins 0.27) - brace-expansion, js-yaml, dompurify, fast-uri, fast-xml-parser: patched via npm audit fix; next 16.2.9 -> 16.2.11 (multiple advisories) - docs: add esbuild/sharp overrides and raise dompurify/fast-uri/ fast-xml-parser override floors to the patched versions --- docs/package-lock.json | 669 ++++++++++++++++++++++------------------- docs/package.json | 8 +- package-lock.json | 556 ++++++++++++++++++---------------- package.json | 2 +- 4 files changed, 667 insertions(+), 568 deletions(-) diff --git a/docs/package-lock.json b/docs/package-lock.json index b8e8fda..a7b217d 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -329,9 +329,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -345,9 +345,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -361,9 +361,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -377,9 +377,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -393,9 +393,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -409,9 +409,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -425,9 +425,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -441,9 +441,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -457,9 +457,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -473,9 +473,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -489,9 +489,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -505,9 +505,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -521,9 +521,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -537,9 +537,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -553,9 +553,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -569,9 +569,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -585,9 +585,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -601,9 +601,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -617,9 +617,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -633,9 +633,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -649,9 +649,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -665,9 +665,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -681,9 +681,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -697,9 +697,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -713,9 +713,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -729,9 +729,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -1094,9 +1094,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -1106,19 +1106,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -1128,19 +1128,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -1154,9 +1173,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -1170,9 +1189,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], @@ -1186,9 +1205,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], @@ -1202,9 +1221,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], @@ -1218,9 +1237,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], @@ -1234,9 +1253,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], @@ -1250,9 +1269,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -1266,9 +1285,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], @@ -1282,9 +1301,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], @@ -1298,9 +1317,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], @@ -1310,19 +1329,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], @@ -1332,19 +1351,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], @@ -1354,19 +1373,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], @@ -1376,19 +1395,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], @@ -1398,19 +1417,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -1420,19 +1439,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], @@ -1442,19 +1461,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], @@ -1464,38 +1483,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -1505,16 +1540,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -1524,16 +1559,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -1543,7 +1578,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1676,9 +1711,9 @@ } }, "node_modules/@next/env": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.9.tgz", - "integrity": "sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.11.tgz", + "integrity": "sha512-0do5A3BJ2gxWr0ZCMcD6BhW+e595jyxdTl3rXTS6lOtD8ektMiW6CO+EPwt1Eca1DBnm90r/7GdiKWBKxH++DA==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { @@ -1692,9 +1727,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.9.tgz", - "integrity": "sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.11.tgz", + "integrity": "sha512-wryL4pjKmDwGv2ox6+GZDFxvmtSRLqApBR8kL1j4+vhB7Z5vJC/zAnXpiR9Xkfzl0AS8WLMnsuGV/UKI67/rrw==", "cpu": [ "arm64" ], @@ -1708,9 +1743,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.9.tgz", - "integrity": "sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.11.tgz", + "integrity": "sha512-aZl2j4f/fLyjQvOhv0Oe9UaMAQHolYpKhctsoYzplSumKJKPUmgjcf6545aBtysLTcu994TREd0+pSgNE4ohmg==", "cpu": [ "x64" ], @@ -1724,9 +1759,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.9.tgz", - "integrity": "sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.11.tgz", + "integrity": "sha512-5jEriyEnH/LWFy27L2ZG0XaLlyEJIjhsImEsiS9P563PKEVp2BVups/xfOucIrsvVntp11oNcZwjHvaDPYVB5g==", "cpu": [ "arm64" ], @@ -1740,9 +1775,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.9.tgz", - "integrity": "sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.11.tgz", + "integrity": "sha512-eIjcpx2fnnFSSkZDbTxy74KnokUXDjfoLClpWelfgHLf621aTqswhwXQ7GkD5K5rplrS6LZ/Bj+mVuvzluBOEg==", "cpu": [ "arm64" ], @@ -1756,9 +1791,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.9.tgz", - "integrity": "sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.11.tgz", + "integrity": "sha512-8WgzpaWMs46qJT9kiV47cje86L0x/Mu9t8/Gwj+pnbgW3rETVfCnaScPjlYUwNScpOozdcIMHWmAvuZJUonR2w==", "cpu": [ "x64" ], @@ -1772,9 +1807,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.9.tgz", - "integrity": "sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.11.tgz", + "integrity": "sha512-I3UgPds7G4ZYnTb/H+5GBGuUT2DhAk6j0mL6A4s63RjFs74wB2hOWP0vaxsK+3NJraExt3eYEPQ/UtT0x/64Nw==", "cpu": [ "x64" ], @@ -1788,9 +1823,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.9.tgz", - "integrity": "sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.11.tgz", + "integrity": "sha512-n89CjtcThnjrwgJMAiI5xbqwLY51zvwC9tSlArmVndAJLYVl9T9UAdlkXTmZvE++idoXe8KdglQlhNRdUp1c6g==", "cpu": [ "arm64" ], @@ -1804,9 +1839,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.9.tgz", - "integrity": "sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.11.tgz", + "integrity": "sha512-md8CLNggS1Dx9pUgApzps5uAf+N8GN9xywzmNx9vHAWo94HtBwCCqkSnhIrdfQe83Dhz8Lfo/20Nb1Zxal092w==", "cpu": [ "x64" ], @@ -1820,9 +1855,9 @@ } }, "node_modules/@nodable/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", "funding": [ { "type": "github", @@ -3776,16 +3811,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -4830,9 +4865,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -5919,9 +5954,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", - "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -6228,9 +6263,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -6240,32 +6275,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -6869,9 +6904,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -6901,9 +6936,9 @@ } }, "node_modules/fast-xml-parser": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.9.3.tgz", - "integrity": "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", "funding": [ { "type": "github", @@ -6912,17 +6947,32 @@ ], "license": "MIT", "dependencies": { - "@nodable/entities": "^2.2.0", + "@nodable/entities": "^3.0.0", "fast-xml-builder": "^1.2.0", - "is-unsafe": "^1.0.1", - "path-expression-matcher": "^1.5.0", + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", "strnum": "^2.4.1", - "xml-naming": "^0.1.0" + "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, + "node_modules/fast-xml-parser/node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -8270,9 +8320,9 @@ } }, "node_modules/is-unsafe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz", - "integrity": "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", "funding": [ { "type": "github", @@ -8408,9 +8458,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "funding": [ { "type": "github", @@ -10215,13 +10265,13 @@ "license": "MIT" }, "node_modules/next": { - "version": "16.2.9", - "resolved": "https://registry.npmjs.org/next/-/next-16.2.9.tgz", - "integrity": "sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==", + "version": "16.2.11", + "resolved": "https://registry.npmjs.org/next/-/next-16.2.11.tgz", + "integrity": "sha512-B339zaqbyK8cmxhoAvLrcwoabwCP1wz21zSzfqxqXAemTu2BXnH7tQnfcglKv1vnMUIDBc+Hth7XODQriTZiRQ==", "license": "MIT", "peer": true, "dependencies": { - "@next/env": "16.2.9", + "@next/env": "16.2.11", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", @@ -10235,14 +10285,14 @@ "node": ">=20.9.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "16.2.9", - "@next/swc-darwin-x64": "16.2.9", - "@next/swc-linux-arm64-gnu": "16.2.9", - "@next/swc-linux-arm64-musl": "16.2.9", - "@next/swc-linux-x64-gnu": "16.2.9", - "@next/swc-linux-x64-musl": "16.2.9", - "@next/swc-win32-arm64-msvc": "16.2.9", - "@next/swc-win32-x64-msvc": "16.2.9", + "@next/swc-darwin-arm64": "16.2.11", + "@next/swc-darwin-x64": "16.2.11", + "@next/swc-linux-arm64-gnu": "16.2.11", + "@next/swc-linux-arm64-musl": "16.2.11", + "@next/swc-linux-x64-gnu": "16.2.11", + "@next/swc-linux-x64-musl": "16.2.11", + "@next/swc-win32-arm64-msvc": "16.2.11", + "@next/swc-win32-x64-msvc": "16.2.11", "sharp": "^0.34.5" }, "peerDependencies": { @@ -10599,9 +10649,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.0.tgz", - "integrity": "sha512-e5y7RCLHKjemsgQ4eqGJtPyr10ILz25HO7flzxhTV8bgvd5yHx98DGtCAtbVW9f2TqnYI/gEVZd+vz7snrdPTw==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", "funding": [ { "type": "github", @@ -11521,48 +11571,53 @@ } }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "optional": true, "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { diff --git a/docs/package.json b/docs/package.json index 6c7dc70..2fd939b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -41,10 +41,12 @@ "typescript": "^5.9.3" }, "overrides": { - "dompurify": "^3.4.11", + "dompurify": "^3.4.12", "uuid": "^11.1.1", - "fast-uri": "^3.1.2", - "fast-xml-parser": "^5.7.0", + "fast-uri": "^3.1.4", + "fast-xml-parser": "^5.10.1", + "esbuild": "^0.28.1", + "sharp": "^0.35.3", "next": { "postcss": "^8.5.10" } diff --git a/package-lock.json b/package-lock.json index 3b22f36..3f5b74e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,7 @@ "prettier": "3.6.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", - "sharp": "^0.34.5", + "sharp": "^0.35.3", "svelte": "^5.55.7", "svelte-check": "^4.0.0", "tailwindcss": "^3.4.17", @@ -55,9 +55,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", - "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "dev": true, "license": "MIT", "optional": true, @@ -66,9 +66,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", - "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -83,9 +83,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", - "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -100,9 +100,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", - "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -117,9 +117,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", - "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -134,9 +134,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", - "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -151,9 +151,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", - "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -168,9 +168,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", - "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -185,9 +185,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", - "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -202,9 +202,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", - "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -219,9 +219,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", - "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -236,9 +236,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", - "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -253,9 +253,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", - "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -270,9 +270,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", - "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -287,9 +287,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", - "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -304,9 +304,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", - "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -321,9 +321,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", - "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -338,9 +338,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", - "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -355,9 +355,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", - "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -372,9 +372,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", - "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -389,9 +389,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", - "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -406,9 +406,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", - "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -423,9 +423,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", - "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -440,9 +440,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", - "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -457,9 +457,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", - "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -474,9 +474,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", - "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -491,9 +491,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", - "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -783,9 +783,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", "cpu": [ "arm64" ], @@ -796,19 +796,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.2" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", "cpu": [ "x64" ], @@ -819,19 +819,39 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", "cpu": [ "arm64" ], @@ -846,9 +866,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", "cpu": [ "x64" ], @@ -863,9 +883,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", "cpu": [ "arm" ], @@ -880,9 +900,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", "cpu": [ "arm64" ], @@ -897,9 +917,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", "cpu": [ "ppc64" ], @@ -914,9 +934,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", "cpu": [ "riscv64" ], @@ -931,9 +951,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", "cpu": [ "s390x" ], @@ -948,9 +968,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", "cpu": [ "x64" ], @@ -965,9 +985,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", "cpu": [ "arm64" ], @@ -982,9 +1002,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", "cpu": [ "x64" ], @@ -999,9 +1019,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", "cpu": [ "arm" ], @@ -1012,19 +1032,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.2" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", "cpu": [ "arm64" ], @@ -1035,19 +1055,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.2" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", "cpu": [ "ppc64" ], @@ -1058,19 +1078,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.2" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", "cpu": [ "riscv64" ], @@ -1081,19 +1101,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.2" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", "cpu": [ "s390x" ], @@ -1104,19 +1124,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.2" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", "cpu": [ "x64" ], @@ -1127,19 +1147,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", "cpu": [ "arm64" ], @@ -1150,19 +1170,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", "cpu": [ "x64" ], @@ -1173,39 +1193,56 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", "cpu": [ "wasm32" ], "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", "cpu": [ "arm64" ], @@ -1216,16 +1253,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", "cpu": [ "ia32" ], @@ -1236,16 +1273,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", "cpu": [ "x64" ], @@ -1256,7 +1293,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -2103,16 +2140,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", - "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", + "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -2379,9 +2416,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", + "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2672,9 +2709,9 @@ "license": "MIT" }, "node_modules/dompurify": { - "version": "3.4.11", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", - "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", + "version": "3.4.12", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.12.tgz", + "integrity": "sha512-zQvGet8Z2sWbQhCmfFz/T5QWH2oBmjnqK3qvOjaqaNLrLEF912WamU+ohnTp0TCep/MFVHpdJuCZEdFOdTnEFg==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -2698,9 +2735,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.7", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", - "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2711,32 +2748,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.7", - "@esbuild/android-arm": "0.27.7", - "@esbuild/android-arm64": "0.27.7", - "@esbuild/android-x64": "0.27.7", - "@esbuild/darwin-arm64": "0.27.7", - "@esbuild/darwin-x64": "0.27.7", - "@esbuild/freebsd-arm64": "0.27.7", - "@esbuild/freebsd-x64": "0.27.7", - "@esbuild/linux-arm": "0.27.7", - "@esbuild/linux-arm64": "0.27.7", - "@esbuild/linux-ia32": "0.27.7", - "@esbuild/linux-loong64": "0.27.7", - "@esbuild/linux-mips64el": "0.27.7", - "@esbuild/linux-ppc64": "0.27.7", - "@esbuild/linux-riscv64": "0.27.7", - "@esbuild/linux-s390x": "0.27.7", - "@esbuild/linux-x64": "0.27.7", - "@esbuild/netbsd-arm64": "0.27.7", - "@esbuild/netbsd-x64": "0.27.7", - "@esbuild/openbsd-arm64": "0.27.7", - "@esbuild/openbsd-x64": "0.27.7", - "@esbuild/openharmony-arm64": "0.27.7", - "@esbuild/sunos-x64": "0.27.7", - "@esbuild/win32-arm64": "0.27.7", - "@esbuild/win32-ia32": "0.27.7", - "@esbuild/win32-x64": "0.27.7" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -3354,9 +3391,9 @@ } }, "node_modules/js-yaml": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", - "integrity": "sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, "funding": [ { @@ -4460,48 +4497,53 @@ "license": "MIT" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "dev": true, - "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shebang-command": { @@ -5070,14 +5112,14 @@ "license": "MIT" }, "node_modules/vite": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", - "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", "dev": true, "license": "MIT", "peer": true, "dependencies": { - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", diff --git a/package.json b/package.json index 84dfc8b..8e627a1 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ "prettier": "3.6.2", "prettier-plugin-svelte": "^3.3.3", "prettier-plugin-tailwindcss": "^0.6.11", - "sharp": "^0.34.5", + "sharp": "^0.35.3", "svelte": "^5.55.7", "svelte-check": "^4.0.0", "tailwindcss": "^3.4.17",