DEV Community

Cover image for Why your open-source fork can get a cease-and-desist (and how to fix it)
Alan West
Alan West

Posted on

Why your open-source fork can get a cease-and-desist (and how to fix it)

Last month a maintainer friend pinged me at 2 a.m. asking what to do about a cease-and-desist letter. They had forked a popular open-source project, kept the original name with a -community suffix slapped on the end, and the upstream company's lawyers were not amused. The code was MIT-licensed. The trademark was not.

This is the kind of problem that catches every first-time forker off guard. You read LICENSE, see something permissive, and assume you're free to do whatever you want. You are — with the code. The brand, the logo, the project name? Those are a completely different legal animal, and getting them wrong can sink your fork before it ships its first release.

Let me walk through how I've helped a few maintainers untangle this, and how to avoid it in the first place.

The problem: a permissive license is not a permissive brand

Here's the scenario. You fork an upstream project because development has stalled, or because you disagree with a direction change, or because you want to add features the maintainers won't accept. You publish your fork under the same license. You keep most of the original name to help users find it.

A few weeks later a letter shows up. Or a polite-but-firm email. Or a takedown notice to your package registry. Suddenly you're scrambling to rename a project that already has stargazers, downstream packages, and CI pipelines pointing at it.

I've watched this play out three times in the last two years, and every time the root cause was the same.

Root cause: trademark and copyright are separate beasts

Software licenses cover copyright. Trademarks cover identity. They don't overlap, and one does not imply the other.

A project can release source code under Apache 2.0, MIT, or GPL-anything, and the trademark — the name, logo, sometimes the visual style — can still be held tight by the original author or company. Apache 2.0 even spells this out in section 6: "This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor."

MIT and BSD don't mention trademarks at all, which a lot of people read as "so it must be allowed." It isn't. Trademark law applies independently of whatever license the code uses. Silence in the license file is not consent.

So when your fork ships under a name that's confusingly similar to the upstream brand, you can be on the wrong side of trademark law even when you're squeaky clean on copyright.

Step 1: Audit every reference to the upstream brand

Before you rename anything, you need to know the full surface area. The name will be in more places than you expect: package manifests, README files, error messages, log output, environment variable prefixes, CSS class names, asset filenames, binary metadata, generated documentation, telemetry strings.

I start with a ripgrep sweep, case-insensitive, against the entire tree:

# Replace UPSTREAM_NAME with the actual brand string
rg -i --hidden --no-ignore \
  -g '!.git' \
  -g '!node_modules' \
  -g '!target' \
  -g '!dist' \
  'upstream_name' > brand-audit.txt

# Count by file extension so you can plan the scrub
rg -i -l 'upstream_name' \
  -g '!.git' \
  | awk -F. '{print $NF}' \
  | sort | uniq -c | sort -rn
Enter fullscreen mode Exit fullscreen mode

The second command is the one that surprises people. You'll typically find hits in extensions you forgot existed in your repo — .proto, .svg, .po, .plist, .desktop, .snap. Each of those needs a plan.

Don't forget binary assets. Icons, splash images, and bundled fonts can carry the brand visually. Run them through your eyes, not grep.

Step 2: Pick a new name and check it's actually clean

The temptation is to pick something cute that obviously references the original. Resist it. "X-but-free" or "X-community" reads as a derivative, and trademark law cares about consumer confusion, not technical accuracy.

My rough checklist before committing to a name:

  • Search the relevant trademark database for your jurisdiction (USPTO TESS in the US, EUIPO eSearch in the EU).
  • Check the major package registries — npm, PyPI, crates.io, Maven Central, Docker Hub — for existing namespaces.
  • Check GitHub for orgs and repos already using the name.
  • Buy the .com, .dev, or .org domain before you announce. Squatters watch GitHub.
  • Run the name past a couple of people who aren't in your bubble. "Is this obviously a fork of X?" If yes, keep brainstorming.

None of this is bulletproof legal advice. But it gets you out of the obvious traps.

Step 3: Do the rename in a single, reviewable commit

Mass renames are where projects get into trouble. Half-renamed codebases ship with confusing identifiers for years. Do it in one pass.

Here's the pattern I use for the code-side rename. Adjust for your case sensitivity needs:

#!/usr/bin/env bash
set -euo pipefail

OLD='OldName'
NEW='NewName'
OLD_LC=$(echo "$OLD" | tr '[:upper:]' '[:lower:]')
NEW_LC=$(echo "$NEW" | tr '[:upper:]' '[:lower:]')

# Rename file *contents* — preserve case variants separately
rg -l "$OLD" --hidden -g '!.git' | xargs sed -i '' "s/${OLD}/${NEW}/g"
rg -l "$OLD_LC" --hidden -g '!.git' | xargs sed -i '' "s/${OLD_LC}/${NEW_LC}/g"

# Rename file and directory *names* (deepest paths first to avoid breaking moves)
find . -depth -name "*${OLD}*" -not -path './.git/*' | while read -r path; do
  new=$(echo "$path" | sed "s/${OLD}/${NEW}/g")
  mv "$path" "$new"
done
Enter fullscreen mode Exit fullscreen mode

A few warnings from experience. The sed -i '' syntax is for BSD sed (macOS); GNU sed wants sed -i with no argument. Run this on a clean branch with no uncommitted work, and review the diff carefully — a global replace will catch substrings you didn't intend, like a variable named oldname_helper becoming newname_helper when you wanted to leave it alone.

For package manifests, do the manifest fields explicitly rather than relying on the global replace:

{
  "name": "newname",
  "description": "A community-maintained slicing toolkit",
  "repository": {
    "type": "git",
    "url": "https://github.com/newname-org/newname.git"
  },
  "bugs": {
    "url": "https://github.com/newname-org/newname/issues"
  },
  "homepage": "https://newname.org"
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Replace branded assets and metadata

The code is the easy part. The assets are where people slip up.

  • Replace logos and icons with originals. Don't "edit" the existing ones — start from scratch or commission new art.
  • Strip EXIF metadata from images: exiftool -all= path/to/image.png.
  • Regenerate any compiled binary metadata. On macOS that means rewriting Info.plist. On Windows, version resources in the binary. On Linux desktop builds, the .desktop file's Name= and Icon= entries.
  • Audit your fonts. Some bundled font licenses prohibit redistribution under a renamed product.

Prevention: a checklist before you fork

If you're standing at the trailhead of a new fork, save yourself the 2 a.m. lawyer email:

  • Read the upstream TRADEMARK.md or trademark policy if one exists. Many projects publish explicit fork guidelines.
  • Treat the project name as off-limits by default. Pick your own from day one.
  • Verify your chosen name on registries and domains before your first public commit.
  • Keep contributions under a clear CLA or DCO so your provenance story is clean if questions come up later.
  • Document, in your own README, exactly which upstream commit you forked from and what's changed. This protects you both technically and reputationally.

Forking is one of the most powerful things open source gives us. But the freedom to fork the code is not the same as the freedom to inherit the name. Treat them as two separate decisions, and you'll save yourself a lot of grief.

Top comments (0)