A desktop commitment gate. You write a task; coding tools stay locked until it is done.
When you finish, the app always texts the person you named. Tools unlock after that text is accepted.
If you give up instead, you choose:
- I can give up without a reply — unlock from this computer. They still got (or will get) the completion texts; they do not have to text yes.
- They have to reply yes if I give up — no unlock button on this computer. Their yes is recorded on a website you do not control.
Existing installs that already had texts plus the yes/no website stay on the hard give-up path until you switch.
The app never invents tasks. You create them.
Desktop UI (Tauri + React)
↓
Local enforcement service (Rust)
↓
Commitment state (SQLite + HMAC lockfile)
FastAPI backend: override authorization + Sendblue inbound webhooks
Closing the GUI does not unlock tools. The background enforcer keeps watching processes.
If you want giving-up to require their yes (rather than unlocking yourself), see docs/DEPLOY.md for hosting the yes/no website. Everyone still sets up texts in the app. Tag a release so GitHub builds the Windows and macOS installers.
The rest of this file is for working on the code.
- Rust (stable)
- Node.js 20+
- Python 3.11+
- A Sendblue account (iMessage / SMS)
From the repo root:
python scripts/make_icons.py
cargo build -p ysnc-enforcer
cd backend
python -m venv .venv
.\.venv\Scripts\activate
pip install -r requirements.txt
copy .env.example .env
python ..\scripts\gen_secrets.py # paste the three secrets into .env
# then add your Sendblue key id, secret and from-number
uvicorn app.main:app --app-dir . --host 127.0.0.1 --port 8787The backend refuses to start until OVERRIDE_SIGNING_SECRET, SENDBLUE_WEBHOOK_SECRET and
CLIENT_API_TOKEN are set to real values. An unauthenticated webhook would let anyone approve
an override, so there is no permissive default.
In a second terminal:
cd desktop
npm install
npm run tauri devThe desktop app will try to spawn ysnc-enforcer. On first launch, Setup asks you to install the background lock so it survives reboot.
Same steps, with:
python3 -m venv .venv
source .venv/bin/activate
cp .env.example .env
uvicorn app.main:app --host 127.0.0.1 --port 8787The enforcer installs a LaunchAgent at ~/Library/LaunchAgents/com.youshallnotcode.enforcer.plist.
Open the app. Setup always asks for you, a person, texts, and the background lock.
When you finish a task, that person gets a text. Every time.
Then it asks what happens if you give up:
I can give up without a reply — those four steps. No website.
They have to reply yes if I give up — the same four, plus the public yes/no website URL and the two passwords from your host.
Hosting steps for that extra website are in docs/DEPLOY.md. Settings is for later edits, including switching the give-up rule.
Credentials go to the OS keychain (Windows Credential Manager / macOS Keychain). Where no keychain
is available, or when YSNC_DATA_DIR is set for a self-contained install, they are files in the app
data folder instead — owner-only on macOS and Linux. Settings states which of the two is in use
rather than claiming the keychain unconditionally. Nothing is hard-coded.
- Create a commitment: Send 5 cold emails before coding
- Coding agents (Codex, Claude Code, Cursor, Windsurf, Gemini CLI, Aider, plus any custom executable) are blocked
- Try launching one → notification: CODING LOCKED / Complete first: …
- Do the task outside the app
- Press I finished
- Sendblue sends the completion message — this happens in both modes
- On Sendblue success, tools unlock immediately
If you chose I can give up without a reply, I didn’t — unlock myself still texts them the override notice, then unlocks. They do not have to reply.
If you chose They have to reply yes if I give up:
- Press Request override and give a reason
- That person receives the request and replies
APPROVE ABC123orDENY ABC123 - Tools stay locked until the backend records a signed approval
- The local app cannot approve an override
For a safe first block test, add notepad.exe (Windows) or TextEdit (macOS) as a custom executable instead of locking Cursor while you are still developing this repo.
The backend must be reachable by Sendblue:
POST /webhooks/sendblue
Point a public HTTPS URL (Cloudflare Tunnel, ngrok, or a deployed host) at that path, then configure it as a Sendblue receive webhook with SENDBLUE_WEBHOOK_SECRET.
An inbound reply is accepted only when both hold:
- the sender is one of the numbers the request was actually sent to, and
- the reply quotes the code for one of that sender's pending requests.
A wrong code is a refusal, never a fallback to the sender's most recent request. Registering an override for a commitment that already has one is rejected if it names a different contact, so a pending request cannot be re-pointed at a friendlier approver.
The desktop enforcer polls GET /overrides/{id} (bearer token) and unlocks only when the HMAC
signature verifies and the approver is one of that commitment's contact numbers. There is no
local approve API.
If the backend was unreachable when the request was made, the enforcer notices the missing registration on its next poll and re-registers it; Today shows a warning until it lands.
The lock does not live in the database alone. A signed lock.json records which database it
belongs to and which commitments were holding the lock, so:
- deleting or replacing
ysnc.dbis detected instead of unlocking, - deleting a locked row is detected,
- editing any commitment field (including evidence, delivery status and override metadata) breaks its signature,
- an unreadable
lock.jsoncounts as locked.
A detected tamper writes tamper.json and keeps everything locked across restarts. In someone else mode, clearing it needs the same external approval as any override — Today shows a panel to ask a contact — and your person is texted once when it happens. In I’ll unlock myself mode, Home has a button to clear it here.
Wiping the whole app data folder is uninstall, and uninstall stays easy on purpose.
cd desktop && npm run sidecar # stage the enforcer; the desktop crate needs it
cd .. && cargo test --workspace --all-targets # core, enforcer, packaging guards
cd backend && pip install -r requirements-dev.txt && python -m pytest -q
cd ../desktop && npx tsc --noEmitnpm run sidecar builds ysnc-enforcer and stages it where Tauri expects it.
npm run tauri build runs it automatically; a bare cargo build of the desktop
crate does not, and fails until it has been run once.
This is not malware-style persistence.
- Remove login item in Settings (only while unlocked)
- Delete the app
- Windows:
HKCU\Software\Microsoft\Windows\CurrentVersion\Run\YouShallNotCodeEnforcer - macOS: unload and delete
~/Library/LaunchAgents/com.youshallnotcode.enforcer.plist
Sendblue only. The provider is behind a small MessagingProvider trait / protocol so another vendor could be added later. Twilio is not integrated.
AI-assisted work in this repo uses gstack, vendored as a git submodule at gstack/. After clone:
git submodule update --init --depth 1 gstackCursor wrappers live in .cursor/skills/. Slash commands such as /design-review load gstack/<skill>/SKILL.md.
Do the hard task and publicly declare it, or admit failure and ask another human.
Quietly escaping into coding tools is the option this app removes.