Dependency licenses
Last updated: Oct 28, 2026
Rationale
All the third-party dependencies of Fluid Attacks, in all components and languages, must have one of the licenses below. The list uses SPDX identifiers. Thus, the list is clear, and a machine can check it. It is not a matter of interpretation.
- Public-domain equivalent:
0BSD,CC0-1.0,MIT-0,Unlicense. - Permissive:
Apache-2.0,Apache-2.0 WITH LLVM-exception,BSD-2-Clause,BSD-3-Clause,BSL-1.0,BlueOak-1.0.0,ISC,MIT,NCSA,Unicode-3.0,Unicode-DFS-2016,Zlib. - Permissive, for data sets:
CDLA-Permissive-2.0. - File-level copyleft:
MPL-2.0.
There are 18 identifiers in total.
The canonical machine-readable copy is in
develops/utils/rust/checks/canonical/deny.toml.
This page is the human-readable statement of the same policy.
Fluid Attacks accepts only these licenses because it ships proprietary software. It delivers its components to customers as hosted services and as compiled binaries. Thus, it can depend only on code with a license that makes no claim on the code that links to it. Each accepted category satisfies this condition:
- Public-domain equivalent and permissive licenses ask only for attribution. They put no obligation on the source code of Fluid Attacks.
MPL-2.0is the one reciprocal license that Fluid Attacks accepts. Its obligations apply to each file. If you change an MPL-2.0 file, you must publish that file. When the code of Fluid Attacks includes an MPL-2.0 file, the obligations do not apply to the other files. This boundary is compatible with proprietary distribution.
Thus, Fluid Attacks does not accept these three categories:
- Strong copyleft (
GPL-2.0,GPL-3.0,AGPL-3.0). Reciprocity applies to the full derived work. Thus, Fluid Attacks would have to publish the components that link it.AGPL-3.0applies in more conditions: network use starts its obligations, not distribution. Fluid Attacks delivers its platform through the network. - Linking-level copyleft (
LGPL-2.1,LGPL-3.0). The exemption of these licenses applies only if you can replace the dependency at link time. The binaries of Fluid Attacks are statically linked. To keep the exemption, Fluid Attacks must ship the object files of each binary. With these files, a customer can link the binary again with a changed version. Fluid Attacks does not do this. Thus, the practical effect is closer to the GPL than to a permissive license. - Proprietary, source-available, and unlicensed code. This includes code with a bespoke commercial license, a source-available license (for example, the Business Source License), or no license. Without a license, by default, you get no rights.
This policy is the dependency-level equivalent of the preference for open source in the engineering philosophy of Fluid Attacks. Fluid Attacks uses much open source, but only where its terms let Fluid Attacks make its own licensing decisions.
Alternatives
Fluid Attacks examined these other policies. The list starts with the most interesting policy for the requirements of Fluid Attacks.
Accepting strong copyleft with isolation rules
This policy accepts GPL and AGPL dependencies,
with a rule: you can use them only as different processes
or through a network boundary.
- It increases the number of libraries that you can use, and most of the tools in some categories have GPL licenses.
- A legal decision, not a mechanical check, tells if an integration makes a derived work. Thus, no CI job can make this decision, and each case becomes a review.
- The dependency manifest does not show the isolation that makes this policy defensible. The team can add a library dependency today and link it directly tomorrow, and nothing shows the change.
- Network use starts the obligations of
AGPL-3.0, and Fluid Attacks delivers most of its components through the network. Thus, the process boundary that neutralizes the GPL does not help. - The obligation goes to the incorrect team. Engineering would have a question that only a legal review can resolve, in the short time of a dependency update.
Reviewing each dependency case by case
This policy has no list. A person makes a decision each time that someone adds a dependency.
- It is very flexible, and it can accept unusual licenses easily.
- The work increases with the number of dependencies, not with the number of different licenses. There are many more transitive dependencies than direct dependencies.
- The decisions are not in a location where a future reader can find them. Thus, the team examines the same license again, and different components get different answers.
- The safe path becomes slower than the unsafe path. This is the incorrect incentive for a check that must prevent an irreversible error.
A per-component allow list
With this policy, each component declares the licenses that it accepts, because a CLI that customers install and an internal service have different exposure.
- It lets you use a riskier dependency in a component where the obligation cannot start.
- The difference that this policy uses is not stable. Internal tools become customer-facing, and libraries move between components. Thus, a dependency that the rules of one component accepted can get the exposure of a different component, and nobody sees the change.
- It multiplies the review work by the number of components, but the legal question does not change.
- At this time, Fluid Attacks uses shared configuration as canonical, and it compares each component with it. Thus, a per-component list would be the only location where components can disagree on a legal matter.
Usage
The policy applies to all the components. The enforcement does not apply to all of them at this time, and it is important to be clear about this difference:
| Ecosystem | Gate | Status |
|---|---|---|
| Rust | cargo-deny | Enforced |
| TypeScript | pnpm licenses list | Not enforced |
| Python | pip-licenses | Not enforced |
In Rust,
cargo-deny
checks the resolved dependency graph against [licenses].allow in each lint
run.
Also, the list is the same for all the components.
Each component commits its own deny.toml,
and the conformance check compares that file with the canonical file.
Thus, if a component adds a license to its own list, the conformance check shows an error.
The other two rows name a capability, not a check that runs at this time.
pnpm licenses list
shows the licenses of an installed tree, but it has no policy concept.
Thus, Fluid Attacks would have to write the comparison.
pip-licenses
can fail a build directly with --allow-only.
These two gates are weaker than the Rust gate,
because the upstream metadata is weaker.
Crates.io validates the license field of each published crate
as an SPDX expression.
Thus, cargo-deny can evaluate compound terms,
for example, Apache-2.0 OR MIT.
The other registries do not guarantee this.
npm uses the license field as a convention.
In Python,
PEP 639
standardized an SPDX License-Expression field,
but this field is optional,
and you can also use free-text values and classifiers.
A gate in those ecosystems compares license names and does not
evaluate expressions.
Thus, it must have a normalization step, which Rust gets from the registry automatically.
Requesting an exception
If the license of a dependency is not on the list, there are two paths, and they are not equivalent:
- Only one dependency uses the license.
Grant the exception only for that dependency.
In Rust, this is a
[licenses].exceptionsentry with the name of that crate. The shared list does not change. - All components can use the license. Change the canonical list. Before you do this, update this page. Then, the change goes to all the Rust components at the same time through the conformance check. Thus, it is a policy decision, not a local decision, and it follows the ADR process of the Stack overview.