Build Freedom

KINDAreplaces $3.33/mosaves $39.96/yrback to the verdict

0%0 of 17 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

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.

estimated effort multi-daythe files for this build are in the project pack

BlockingName resolution and connection-level blocking, never traffic interceptionEnforcementA privileged component with a delayed disable pathSessionsTime-bounded with a stated end and no early exit shortcutSyncOptional, so a session started on one device holds on anotherPrivacyBlock decisions logged locally in aggregate, never a browsing history

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. 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

  2. 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

  3. 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

  4. 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

  5. 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.

VariableNeededExampleWhere the value comes from
DB_PATHrequired~/.local/share/blocker/state.dbSessions, schedules and aggregate counts. No browsing history.
EARLY_EXIT_DELAY_MINUTESrequired15Set before a session starts and fixed for its duration.
INSTALL_CERTIFICATErequiredneverPresent as a constant. Reading every connection is disproportionate to blocking a site.
STORE_ATTEMPT_DESTINATIONSrequiredneverPresent as a constant. A per-destination log is the most sensitive record on the machine.
OFFLINE_BEHAVIOURrequiredkeep_enforcingOtherwise turning off the network is the bypass.
SYNC_REMOTEoptionals3://my-blocker/stateOptional. One device works completely without it.

The build, in order

  1. Blocking without reading anything

    Sites are blocked at the resolution and connection layers, with no traffic inspection.

    1. Coarse, and it requires no ability to read anything.

      Files src/resolver.ts

    2. Reading every connection is disproportionate to blocking a website.

      Files tests/no-interception.test.ts

    3. Shared addresses and alternative resolvers are real gaps, not secrets.

      Files docs/limitations.md

    done when · tick each as it passes
    watch 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.
  2. Friction with a real exit

    Ending a session early is difficult and always possible.

    1. Friction is the mechanism. Impossibility is a fault.

      Files src/sessions.ts

    2. Deciding it in the moment defeats the purpose.

      Files src/sessions.ts

    3. The escape route must not depend on the software cooperating.

      Files tests/removal.md

    done when · tick each as it passes
    watch 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.
  3. Sessions and schedules that survive a restart

    A session holds through a reboot and across a schedule boundary.

    1. Restarting is the first bypass anyone finds.

      Files src/persist.ts

    2. A laptop closed at nine and opened at eleven is the normal case.

      Files src/schedule.tssrc/schedule.test.ts

    3. A session that expired while the machine was off should not linger.

      Files src/sessions.ts

    done when · tick each as it passes
    watch 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.
  4. The second device

    A session started on one machine holds on the others.

    1. Otherwise turning off the network is the bypass.

      Files src/sync.ts

    2. A device that was off should catch up rather than miss the session.

      Files src/sync.ts

    3. One device should work completely without an account.

      Files src/sync.ts

    done when · tick each as it passes
    watch 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.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more productivity utilities.