<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ryosuke Aoki</title>
    <description>The latest articles on DEV Community by Ryosuke Aoki (@ryosuke_aoki_2266a6aa25dc).</description>
    <link>https://dev.to/ryosuke_aoki_2266a6aa25dc</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4091560%2Fdbeda166-e2c1-448e-8834-58a28310e220.jpg</url>
      <title>DEV Community: Ryosuke Aoki</title>
      <link>https://dev.to/ryosuke_aoki_2266a6aa25dc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ryosuke_aoki_2266a6aa25dc"/>
    <language>en</language>
    <item>
      <title>Your license scanner is answering the wrong question</title>
      <dc:creator>Ryosuke Aoki</dc:creator>
      <pubDate>Mon, 24 Aug 2026 05:56:30 +0000</pubDate>
      <link>https://dev.to/ryosuke_aoki_2266a6aa25dc/your-license-scanner-is-answering-the-wrong-question-j0c</link>
      <guid>https://dev.to/ryosuke_aoki_2266a6aa25dc/your-license-scanner-is-answering-the-wrong-question-j0c</guid>
      <description>&lt;p&gt;Run a license scanner over a typical Node or Python project and you get a table:&lt;br&gt;
package, license, risk level. &lt;code&gt;GPL-3.0&lt;/code&gt; comes back red. &lt;code&gt;MIT&lt;/code&gt; comes back green.&lt;br&gt;
&lt;code&gt;MPL-2.0&lt;/code&gt; comes back yellow, probably.&lt;/p&gt;

&lt;p&gt;That table is answering a question nobody asked, which is &lt;em&gt;"is this license&lt;br&gt;
scary?"&lt;/em&gt; The question you actually have is &lt;em&gt;"does this obligate me to do&lt;br&gt;
something?"&lt;/em&gt; — and for a large fraction of licenses, the honest answer is: it&lt;br&gt;
depends on facts the scanner never asked you about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Copyleft obligations attach to events, not to code
&lt;/h2&gt;

&lt;p&gt;Read GPL-3.0 looking for the trigger and you find it in section 5: the&lt;br&gt;
requirement to license the whole work under the same terms and make&lt;br&gt;
corresponding source available applies when you &lt;strong&gt;convey&lt;/strong&gt; the work. Conveying&lt;br&gt;
is distribution. If you never distribute, the obligation never fires.&lt;/p&gt;

&lt;p&gt;So consider a hosted SaaS product with a GPL-3.0 library in &lt;code&gt;node_modules&lt;/code&gt;,&lt;br&gt;
running on your own servers, with users reaching it over HTTPS.&lt;/p&gt;

&lt;p&gt;You have not distributed anything. Nobody received a copy. The obligation does&lt;br&gt;
not arise.&lt;/p&gt;

&lt;p&gt;Your scanner said red.&lt;/p&gt;

&lt;p&gt;Now consider the same library, same version, same lockfile, in a desktop app you&lt;br&gt;
ship to customers. Distribution. The obligation fires, and it fires on &lt;em&gt;the&lt;br&gt;
whole work&lt;/em&gt; — your proprietary code included.&lt;/p&gt;

&lt;p&gt;Your scanner said red. Same red. Same shade of red.&lt;/p&gt;

&lt;p&gt;One of those two answers was useless. The scanner had no way to tell them apart,&lt;br&gt;
because it never asked how the software reaches its users.&lt;/p&gt;

&lt;h2&gt;
  
  
  The AGPL exists precisely because that gap is real
&lt;/h2&gt;

&lt;p&gt;The Free Software Foundation noticed the SaaS hole and wrote a license to close&lt;br&gt;
it. AGPL-3.0 section 13 adds an obligation the GPL does not have: if users&lt;br&gt;
interact with a modified version &lt;strong&gt;over a network&lt;/strong&gt;, you must offer them the&lt;br&gt;
corresponding source of the whole work.&lt;/p&gt;

&lt;p&gt;That single clause is the entire practical difference for a hosted service:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Hosted SaaS&lt;/th&gt;
&lt;th&gt;Distributed binary&lt;/th&gt;
&lt;th&gt;Internal only&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GPL-3.0&lt;/td&gt;
&lt;td&gt;no obligation&lt;/td&gt;
&lt;td&gt;obligation fires&lt;/td&gt;
&lt;td&gt;no obligation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AGPL-3.0&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;obligation fires&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;obligation fires&lt;/td&gt;
&lt;td&gt;no obligation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two licenses that most tools file under the same "strong copyleft, high risk"&lt;br&gt;
heading, producing opposite answers for the deployment model most companies&lt;br&gt;
actually use.&lt;/p&gt;

&lt;p&gt;And note the third column. For internal-only software — an internal dashboard,&lt;br&gt;
a build tool, something that never leaves the company — neither license&lt;br&gt;
obligates you to disclose anything. Not because of a loophole, but because&lt;br&gt;
neither distribution nor network interaction with outside users ever happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dev dependencies are a different question entirely
&lt;/h2&gt;

&lt;p&gt;This one causes more unnecessary panic than anything else.&lt;/p&gt;

&lt;p&gt;If a GPL-3.0 package appears in your lockfile as a dev dependency — a test&lt;br&gt;
runner, a linter, a bundler — it is not part of the artifact you ship. No&lt;br&gt;
distribution of &lt;em&gt;that package&lt;/em&gt; occurs. No obligation arises.&lt;/p&gt;

&lt;p&gt;The caveat worth stating out loud: tools that &lt;strong&gt;emit code into your output&lt;/strong&gt; are&lt;br&gt;
a separate case. A code generator whose templates land in your shipped source is&lt;br&gt;
not the same as a linter that only reads your source. Worth checking&lt;br&gt;
individually rather than assuming.&lt;/p&gt;

&lt;p&gt;Most scanners flatten this too. &lt;code&gt;dependencies&lt;/code&gt; and &lt;code&gt;devDependencies&lt;/code&gt; are right&lt;br&gt;
there in the manifest, distinguished by the ecosystem itself, and the report&lt;br&gt;
still shows one risk column.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not every license works this way
&lt;/h2&gt;

&lt;p&gt;It is worth being precise about which licenses this distinction actually&lt;br&gt;
changes, because "it depends" is not useful if it applies to everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It changes the answer for:&lt;/strong&gt; GPL, AGPL, LGPL, and the source-available&lt;br&gt;
licenses like SSPL and BUSL — anything whose trigger is a distribution or&lt;br&gt;
network-interaction event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It does not change the answer for:&lt;/strong&gt; MIT, Apache-2.0, BSD. Permissive licenses&lt;br&gt;
ask for attribution and not much else, in every model. Apache-2.0 adds a patent&lt;br&gt;
grant and a NOTICE requirement, which are obligations, but they do not vary by&lt;br&gt;
how you ship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MPL-2.0 is the interesting middle.&lt;/strong&gt; Its copyleft is per &lt;em&gt;file&lt;/em&gt;, not per&lt;br&gt;
project. Files it covers stay under it and their modifications must be&lt;br&gt;
published; your own files carry whatever terms you choose. It treats static and&lt;br&gt;
dynamic linking alike. So the shipping model barely matters — but for a reason&lt;br&gt;
that is the opposite of the permissive case: the obligation is real, it is just&lt;br&gt;
scoped narrowly enough that it does not reach your code.&lt;/p&gt;

&lt;p&gt;Three genuinely different mechanisms. One risk column cannot represent them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linkage is the second axis
&lt;/h2&gt;

&lt;p&gt;For compiled languages there is a further fact the manifest does not tell you.&lt;/p&gt;

&lt;p&gt;LGPL's whole design is that you may use the library in a proprietary work&lt;br&gt;
provided the user can replace it — which dynamic linking gives you almost for&lt;br&gt;
free, and static linking does not. In Go and Rust, static linking is the&lt;br&gt;
default. So the same LGPL dependency has a materially different answer in a Rust&lt;br&gt;
binary than in a Node application, and nothing in &lt;code&gt;Cargo.lock&lt;/code&gt; says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do about it
&lt;/h2&gt;

&lt;p&gt;Nothing exotic. When you look at a license question, fix three facts first:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;How does this reach users?&lt;/strong&gt; Hosted, distributed binary, delivered to a
customer's environment, internal only, or published as a library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is it in the shipped artifact?&lt;/strong&gt; Runtime or dev-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How is it linked?&lt;/strong&gt; Static or dynamic — only matters for compiled
languages and only for a few licenses.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With those three fixed, most "it depends" answers collapse into one answer. Not&lt;br&gt;
a risk score — an actual yes or no, with the clause that produced it.&lt;/p&gt;

&lt;p&gt;That is also the honest reason a scanner cannot do this for you out of the box:&lt;br&gt;
two of those three facts are not in your repository. They are facts about your&lt;br&gt;
business.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I got annoyed enough at re-deriving this by hand that I built a small free tool&lt;br&gt;
that takes the shipping model as an input and gives the per-model answer with&lt;br&gt;
the clause behind it: &lt;a href="https://licenseguard.tenchorooms.com" rel="noopener noreferrer"&gt;https://licenseguard.tenchorooms.com&lt;/a&gt;. It also runs as an&lt;br&gt;
MCP server if you want your coding agent to stop guessing at this. Source is&lt;br&gt;
Apache-2.0 on GitHub.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Not legal advice. It tells you which clause is implicated and why, which is the&lt;br&gt;
part that is mechanical. Whether it applies to your situation is not.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>programming</category>
      <category>legal</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
