Build Freedom
KINDAreplaces $3.33/mosaves $39.96/yrback to the verdict
A blocking tool whose design problem is asymmetry: it must be harder to disable during a session than it was to start one, while never becoming unremovable, and it must block without acquiring the ability to see what you are doing.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the application and the session logic.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Enforcement requires changing system-level resolution, which is platform-specific and needs a component running with more privilege than the interface.
Get it Your platform's mechanism for a privileged helper, and read what it grants before designing around it.
Verify
Resolution can be changed and restored by the helper - decidefree
Why A blocker with no escape becomes a fault the first time someone needs a blocked site urgently, and the procedure must not depend on the software working.
Get it Write the procedure, test it with the application deliberately broken, and publish it where a user can find it while blocked.
Verify
Removal succeeds with the application non-functional - have readyfree if you have one
Why Enforcement changes system configuration and testing the removal path means deliberately leaving it in a bad state.
Get it A spare machine or a virtual machine you can snapshot and roll back.
Verify
You can install, break and fully restore without losing anything - have readyfree, and it requires being honest with yourself
Why Blocking quality depends entirely on the lists, and a generic list blocks things you need while missing the things you actually reach for.
Get it Write your own from honest observation of where you go when avoiding work, rather than downloading a category list.
Verify
A list exists derived from your own behaviour
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- blocklists(id, name, entries_json, source, updated_at) - sessions(id, started_at, ends_at, blocklist_ids_json, strictness, ended_at, ended_reason) - schedules(id, name, days_json, start_local, end_local, blocklist_ids_json, enabled) - attempts(day, blocklist_id, count) · aggregate counts only, never individual destinations with times - exceptions(id, entry, reason, granted_at, expires_at, requires_delay) · a permanent escape, deliberately awkward - devices(id, name, last_seen_at) · for sessions that span machines - There is deliberately no table recording which sites were attempted and when, because that record would be a browsing history held by a tool whose job is not to watch you.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
DB_PATH | required | ~/.local/share/blocker/state.db | Sessions, schedules and aggregate counts. No browsing history. |
EARLY_EXIT_DELAY_MINUTES | required | 15 | Set before a session starts and fixed for its duration. |
INSTALL_CERTIFICATE | required | never | Present as a constant. Reading every connection is disproportionate to blocking a site. |
STORE_ATTEMPT_DESTINATIONS | required | never | Present as a constant. A per-destination log is the most sensitive record on the machine. |
OFFLINE_BEHAVIOUR | required | keep_enforcing | Otherwise turning off the network is the bypass. |
SYNC_REMOTE | optional | s3://my-blocker/state | Optional. One device works completely without it. |
The build, in order
Blocking without reading anything
Sites are blocked at the resolution and connection layers, with no traffic inspection.
Coarse, and it requires no ability to read anything.
Files
src/resolver.tsReading every connection is disproportionate to blocking a website.
Files
tests/no-interception.test.tsShared addresses and alternative resolvers are real gaps, not secrets.
Files
docs/limitations.md
done when · tick each as it passeswatch out- Intercepting traffic to block by page content requires a certificate that lets the software read every connection the machine makes, which is a capability far exceeding the purpose and a serious risk if the software is ever compromised. Block coarsely at the name and connection layers and accept the reduced precision.
Friction with a real exit
Ending a session early is difficult and always possible.
Friction is the mechanism. Impossibility is a fault.
Files
src/sessions.tsDeciding it in the moment defeats the purpose.
Files
src/sessions.tsThe escape route must not depend on the software cooperating.
Files
tests/removal.md
done when · tick each as it passeswatch out- A blocker with no removal path becomes malware the first time someone needs a blocked site in an emergency and cannot get to it, and the removal procedure must work even if the application itself is broken. Document it, test it, and make sure it does not require the software's cooperation.
Sessions and schedules that survive a restart
A session holds through a reboot and across a schedule boundary.
Restarting is the first bypass anyone finds.
Files
src/persist.tsA laptop closed at nine and opened at eleven is the normal case.
Files
src/schedule.tssrc/schedule.test.tsA session that expired while the machine was off should not linger.
Files
src/sessions.ts
done when · tick each as it passeswatch out- A session that clears on restart makes rebooting the bypass, and it is the first one anyone finds. Persist the session and re-apply blocking before the network is usable, so a restart costs nothing and gains nothing.
The second device
A session started on one machine holds on the others.
Otherwise turning off the network is the bypass.
Files
src/sync.tsA device that was off should catch up rather than miss the session.
Files
src/sync.tsOne device should work completely without an account.
Files
src/sync.ts
done when · tick each as it passeswatch out- A device that loses its connection and therefore stops enforcing turns the network into the bypass, and it is a bypass you can trigger by turning off a router. Fail closed: a device that cannot confirm a session has ended keeps enforcing the last state it knew.
Exceptions and reporting without surveillanceproduct builder
You can unblock something you genuinely need, and the tool never records where you went.
Otherwise the exception path is the instant exit you removed.
Files
src/exceptions.tsA per-destination log is a browsing history of the worst kind.
Files
src/attempts.tsIn the suite, with a deliberately added field to prove the check works.
Files
tests/no-history.test.ts
done when · tick each as it passeswatch out- Logging every blocked attempt with its destination and time produces a browsing history of exactly the sites someone is trying to avoid, held by a tool they installed for self-discipline, and it is the most sensitive record on the machine. Count in aggregate per list per day and store nothing more.
That is the whole plan for Freedom. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Traffic interception for content-level blocking.
- Blocking that cannot be removed.
- Monitoring on behalf of someone else.
- Mobile enforcement with equivalent guarantees.
- polished shortcuts and accessibility
- vendor-maintained updates
- cross-device sync
- deep operating-system edge-case coverage
- Allowlist mode, which is far more effective and far more disruptive
- Session presets for different kinds of work, since the lists genuinely differ
- A weekly aggregate showing which list was hit most, which is useful without being a history
Need the files? The project pack on the verdict page hands your agent the whole brief · more productivity utilities.