California AB 1856: Why Open Source Escaped the Age Verification Law
Hello HaWkers, on August 26, 2026 the California Senate passed AB 1856 by 39 to 0, and the next day the Assembly concurred with the amendments by 69 to 0. Zero votes against in both houses. The text removes anyone distributing software under a free license from the "operating system provider" definition of the Digital Age Assurance Act, the law that takes effect on January 1, 2027 and forces operating systems to collect the user's age.
You maintain an open source project and never thought an American state law could reach you? Well, it almost did. In this article I show what AB 1043 created, the exact wording that saved open source, who is still in scope, and how to audit in practice whether your project's licenses pass the test.
What AB 1043 Created and Why It Scared Open Source
The Digital Age Assurance Act is AB 1043, signed by governor Gavin Newsom on October 13, 2025 and set to take effect on January 1, 2027. Its logic is to move age verification away from the website and onto the operating system.
In practice, the text forces every "operating system provider" to do two things. First, show a screen during initial account setup asking for the date of birth, the age, or both, of the device's primary user. Second, keep a reasonably consistent real time API that returns that user's age bracket to any developer who asks, at the moment the application is downloaded or opened.
There are four brackets: under 13, 13 to under 16, 16 to under 18, and 18 or older. And the app developer cannot simply ignore the signal: the law requires treating the operating system or app store response as the primary indicator of the age bracket, absent clear and convincing evidence to the contrary.
Now read that thinking about Debian. Who is the "provider"? The release team? Each package maintainer? The person mirroring the repository? How does a project with no legal entity, no onboarding screen and no contract with the end user deliver a real time age API? It was an impossible requirement to meet and an expensive one to miss, and that is exactly the alarm the community raised.
The Exemption Text: Two Sentences That Change Everything
AB 1856 does not repeal AB 1043. It surgically rewrites two definitions.
The first one deals with the operating system. "Operating system provider" no longer reaches anyone who distributes an operating system or application under license terms that allow the recipient to copy, redistribute and modify the software.
Notice that the criterion is not a list of approved licenses. It is a functional test: does the license grant copying, redistribution and modification? Then you are out. GPL, MIT, BSD and Apache pass that test effortlessly, which takes Debian, Fedora, Ubuntu, Arch and the BSD family out of the law's scope.
The second definition is just as important and went more unnoticed. "Application" no longer includes software components that are not offered to the consumer as a standalone executable application through a covered app store.
Translating: your npm library, your PyPI package, whatever you publish via apt or pacman and that does not reach the end user as an executable in a store, is also out of the application level obligations. That protects the entire dependency layer, which is where most of us live.
Who Is Out and Who Is Still In
It is worth being precise here, because the headline "California exempts Linux" hides half the story.
Out: Linux distributions, the BSDs and any system or application distributed under a license that allows copying, redistributing and modifying. Also out are the components that do not reach the consumer as a standalone executable in a covered store.
Still in, with the January 1, 2027 deadline holding: Windows, macOS, iOS and Android. In other words, the four systems that the overwhelming majority of end users go through are still required to collect age and expose the signal. The law did not get smaller, it got more precise about who can actually comply with it.
And one step is missing. As of the writing of this article, AB 1856 still depends on the governor's signature to become law. The unanimous approval in both houses is a strong signal, but the process is not over.
How to Audit Your Project's Licenses in Practice
The law's criterion is about the terms you grant to whoever receives the software. That turns into a concrete, verifiable question: does the license field of your project and of your dependencies declare something that allows copying, redistribution and modification?
Start with your own package. The license field accepts an SPDX identifier, and that is what automated tooling reads:
{
"name": "my-project",
"version": "1.4.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/user/my-project.git"
}
}A missing license, or the value UNLICENSED, means you granted nothing. With no express grant of copying, redistribution and modification, the AB 1856 test is not satisfied, and the copyright default is "all rights reserved".
Now the dependency tree. The script below walks node_modules, reads each package's declared license and separates what passes the criterion from what needs a human look:
// audit-licenses.mjs - classifies the licenses in the dependency tree
import { readdirSync, readFileSync, statSync } from 'node:fs'
import { join } from 'node:path'
// Licenses that grant copying, redistributing and modifying
const ALLOWS_REDISTRIBUTION = new Set([
'MIT',
'ISC',
'BSD-2-Clause',
'BSD-3-Clause',
'Apache-2.0',
'MPL-2.0',
'GPL-2.0-only',
'GPL-3.0-only',
'LGPL-3.0-only',
'AGPL-3.0-only',
])
function readPackages(root) {
const found = []
for (const entry of readdirSync(root)) {
const entryPath = join(root, entry)
if (!statSync(entryPath).isDirectory()) continue
// Scopes like @nuxt keep the packages one level below
if (entry.startsWith('@')) {
found.push(...readPackages(entryPath))
continue
}
try {
const pkg = JSON.parse(readFileSync(join(entryPath, 'package.json'), 'utf8'))
found.push({ name: pkg.name, license: pkg.license ?? null })
} catch {
// Directory with no readable package.json: skip it
}
}
return found
}
const packages = readPackages('node_modules')
const toReview = packages.filter((p) => !p.license || !ALLOWS_REDISTRIBUTION.has(p.license))
console.log(`Analyzed: ${packages.length}`)
console.log(`Need manual review: ${toReview.length}`)
for (const p of toReview) {
console.log(` ${p.name} -> ${p.license ?? 'no declared license'}`)
}Two honest caveats about this script. It reads the declared license, not the actual LICENSE file, and there are packages whose package.json lies or uses compound SPDX expressions like (MIT OR Apache-2.0). And the list above is a technical starting point, not legal advice: MPL and AGPL do allow copying, redistribution and modification, but with obligations that change quite a lot about what you take on when you redistribute.
A CI Gate That Fails on Licenses Outside the Criterion
A manual audit ages in a week. The right place for this check is the pipeline, failing the build when a new dependency comes in without a compatible license:
# .github/workflows/licenses.yml
name: License audit
on:
pull_request:
paths:
- 'package.json'
- 'yarn.lock'
- 'package-lock.json'
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
# Install without running package scripts: we only need the metadata
- run: npm ci --ignore-scripts
# The script exits with a non zero code when it finds something pending
- run: node audit-licenses.mjsFor the script to actually block the merge, just exit with an error code when the review list is not empty:
// At the end of audit-licenses.mjs
if (toReview.length > 0) {
console.error('\nDependencies outside the free redistribution criterion.')
console.error('Review them before moving on with the merge.')
process.exit(1)
}The gain here goes well beyond AB 1856. Knowing precisely under which terms each piece of your project reaches the user is the foundation of any compliance conversation, and now it is also the difference between being inside or outside a concrete regulatory obligation.
On the Other Side: Who Will Have to Consume the Age Signal
If you publish an application in a covered store, the exemption does not reach you and you will need to ask the operating system for the signal starting in 2027.
The concrete shape of that API still depends on each vendor. What the law fixes are the four brackets and the obligation to treat the signal as the primary indicator. The signature below is illustrative, so you can isolate that dependency now instead of spreading the regulation across your whole codebase:
// Brackets defined by AB 1043
type AgeBracket = 'under_13' | 'from_13_to_15' | 'from_16_to_17' | 'adult'
interface AgeSignal {
bracket: AgeBracket
source: 'operating_system' | 'store' | 'unavailable'
}
// Single access layer: the rest of the app never talks to the platform
export async function getAgeSignal(): Promise<AgeSignal> {
try {
const raw = await platform.requestAgeSignal()
return { bracket: mapBracket(raw), source: 'operating_system' }
} catch {
// Exempt or unsupported platform: do not break the app
return { bracket: 'adult', source: 'unavailable' }
}
}The catch is not a detail. After AB 1856 there is a whole class of legitimately exempt platforms that will never answer that call. An app that breaks when the signal does not arrive simply stops working on Linux, and that is your bug, not the distribution's.
It is worth centralizing the product decision in a single place, far from the platform call:
// A pure function, easy to test, with the business rule isolated
export function allowsAdultFeature(signal: AgeSignal): boolean {
// With no trustworthy signal, decide by product policy, not by chance
if (signal.source === 'unavailable') return DEFAULT_ADULT_POLICY
return signal.bracket === 'adult'
}What the EFF Still Criticizes
It would be convenient to close this as a clean win, but that is not what happened.
The Electronic Frontier Foundation opposed AB 1856 for two distinct reasons. The first was the disproportionate harm AB 1043 imposed on open source developers, and the exemption solved that one. The second still stands: the foundation argues that any age gating regime hurts freedom of expression, privacy and anonymity for every user, exempt or not.
There was an aggravating factor too. Earlier versions of the text extended the age bracket system to browsers and websites, which would multiply the law's reach. That is where the title of the EFF's article in May 2026 came from, "One Step Forward, Two Steps Back": one step forward for open source, two back for everything else. In July the legislature backed off and removed that expansion, and it was the already trimmed version that passed unanimously in August.
In other words, the text that survived is much better than the one that came in. But the underlying criticism of the age verification model in the operating system is still unanswered.
What This Signals for Software Regulation
The most interesting detail about AB 1856 is technical, not political: the legislature chose to define the exemption by the license terms and not by a list of projects.
A list would age within a year and would turn into a fight over who gets on it. A functional test, "the license allows copying, redistributing and modifying", applies by itself to projects that do not even exist yet. It is the same kind of reasoning the community had been asking for on other fronts, such as the Debian vote on the use of generative AI in code, where the outcome was also to define a criterion instead of a list of allowed tools.
Two practical consequences for the coming months. The first is that your project's license field stopped being package.json bureaucracy and became a fact with legal effect. It is worth checking today whether what is declared matches the repository's LICENSE file.
The second is that this design tends to get copied. When an American state law passes unanimously in both houses, it becomes a drafting model for other jurisdictions. If the "allows copying, redistributing and modifying" criterion settles in as the standard border between regulated and unregulated software, picking a license stops being only a community decision and also becomes a regulatory exposure decision.
Let's go! 🦅
📚 Want to Keep Up With What Is Coming?
This article covered AB 1856 and the open source exemption in California's age verification law, but the ecosystem changes every week and not everything turns into an article here.
On X I share what I am testing, the behind the scenes of my projects and the news that shows up before it becomes a post.
Follow Me There
💡 Daily content about development, career and the tools I actually use

