止损计时(英文版:Time & Effort Check-in)· 运营者 {{OPERATOR}} · 最后更新 2026-09-13
这份文件只描述本产品实际做的事。每一条都能在代码里找到对应实现,文件路径写在括号里,你可以自己对。
产品的全部核心功能——设重评点、立即重评、决定、笔记、历史、复盘、导出——不需要注册,也不需要联网。不登录时数据全部写在你这台设备的浏览器 localStorage 里(键 sl.v2),不经过我们的服务器(public/app/storage.js)。
各浏览器的 localStorage 天然按「站点 + 设备」隔离:同一台电脑上换个浏览器就看不到,别人的设备更看不到。
只有你主动注册并登录,数据才会同步到我们在 Cloudflare Workers KV 上的存储。存的键与内容如下(functions/api/[[route]].js、functions/api/_lib.js):
| 存储键 | 内容 | 保留 |
|---|---|---|
data:{用户名} | 你的事项、轮次、决定、笔记,以及从旧版本迁移过来的原始快照 | 直到你要求删除 |
user:{用户名} | 用户名与口令校验值。口令用 PBKDF2-SHA256、30000 次迭代加盐存储,我们没有你的明文口令 | 直到你要求删除 |
token:{token} | 登录会话 | 30 天后自动过期 |
tokens:{用户名} | 会话反向索引(最多 50 条),只为「改口令/找回后吊销全部会话」而存在 | 同上 |
recovery:{用户名} | 找回码的哈希(PBKDF2 加盐)。明文找回码不落盘,只在生成时给你看一次 | 直到重新生成 |
entitlement:{用户名} / ledger:{用户名} | Pro 权益状态与变更台账(发放、撤销、退款留痕),台账最多 100 条 | 直到你要求删除 |
order:{来源}:{订单号} / redeem:{哈希} | 订单去重标记、兑换码使用状态 | 财务留痕需要,长期保留 |
我们不收集:你的真实姓名、手机号、位置、通讯录、设备标识符、IP 地理画像。注册只要一个你自己起的用户名和一个口令,不要求邮箱。
界面里没有邮箱绑定入口。账户找回只有一条路:注册时给你看一次的 12 位一次性找回码。后端保留了邮箱接口,但没有可用的发信通道,所以不向你展示一个兑现不了的功能(docs/exec/BLOCKERS.md B06)。
接口调用的限流按 Cloudflare 传来的 CF-Connecting-IP 在内存里计数(注册 5 次/小时、登录 10 次/5 分钟等),这个计数不写入存储、进程重启即消失。Cloudflare 作为托管方会按其自身政策保留边缘访问日志,我们不另行留存。
产品分析默认是关闭的(public/app/storage.js 里 analytics: false),关着也能完整使用。你在设置里主动打开后,才会发送以下内容,且仅限这些:
session_started、reassessment_opened、decision_confirmed、resume_started、reminder_scheduled、reminder_trigger_observed、upgrade_viewed、purchase_verified、refund_verified。事项名称、你写的条件与假设、笔记、感受、成果文字、音频——一个字都不上传。 这不是承诺,是实现:服务端 functions/api/events/[[path]].js 逐字段过白名单,白名单之外的字段(包含任何自由文本)在入库前直接丢弃,连带那条事件一起丢。
服务端也不保存事件原文,只按 agg:{日期}:{事件名}:{平台} 累加一个整数计数(外加一个 7 天过期的去重标记 evdedup:{decisionId})。这份数据只能告诉我们「某天有多少次决定确认」,无法还原到某个人做了什么。
关掉开关即刻停止发送,已经累加的计数无法按人回溯删除——因为它从一开始就不知道那是谁。
| 第三方 | 拿到什么 | 为什么 |
|---|---|---|
| Cloudflare(Pages / Workers KV) | 托管页面与接口,存储上面第 2 节列的键 | 本产品的服务器就是它 |
| Lemon Squeezy(海外付款) | 你的付款信息由它处理,我们拿不到你的卡号。它回传给我们的只有订单号、下单邮箱、license key | 它是海外销售的记录商户(Merchant of Record) |
| 微信(小程序内付款) | 付款在微信的虚拟支付里完成,我们只收到微信订单号与金额 | 平台规则要求站内支付 |
除此之外没有别的第三方:没有广告,没有第三方统计 SDK,没有 A/B 测试平台,没有崩溃采集服务。网页不引入任何第三方脚本、字体、图标包或 CSS 库(这是产品的硬约束,见 README.md「设计约束」)。
本站不使用 cookie。 登录令牌与设置存在 localStorage 里,不随请求自动发往任何第三方,也不用于跨站追踪。因此这里没有 cookie 同意弹窗——因为没有要同意的东西。
应用内「导出」会把整包数据存成一个 JSON 文件(文件名形如 stop-loss-timer-20260913-142530.json),内容包括全部事项、轮次、决定、笔记,以及从旧版本迁移时留下的原始快照(public/app/storage.js 的 buildExport())。这是完整的、可读的、不打折的一份。
导出入口在这些地方都有:设置页、容量接近上限时的提示、同步冲突的三选一、以及登录时选择「仅用云端」而放弃本机那份数据的时候——被放弃的那份会直接给你一份导出,不静默丢掉。
sl.v2.u.{用户名})与令牌,不动云端;清除浏览器的站点数据即可清空访客数据。sl.v1.backup.{时间戳} 与旧键 stoploss.v1 一律不自动删除——那是你的回退路径。要清掉就清浏览器站点数据。data:、user:、token:、tokens:、recovery:、email:、entitlement:、ledger: 这几个键下属于你的全部记录,5 个工作日内完成并回信确认。删号会一并删掉 Pro 权益记录,删除后无法凭原账户恢复购买。想保留权益又想清空内容的,请单独说明,我们只删 data:。
订单去重标记(order:)与兑换码使用记录(redeem:)不随删号清除:它们是财务留痕,且不包含你的任何使用内容。
全站走 HTTPS。口令以 PBKDF2-SHA256(30000 次迭代、随用户加盐)存储,我们自己也读不出明文。找回码同样只存哈希。改口令或用找回码重置时,该账户此前的全部登录会话立刻失效。
我们不做「无法被攻破」这种承诺。能说的是:我们没有在服务端存放明文口令、明文找回码,也没有存放任何我们不需要的个人信息。
本产品不面向 14 周岁以下儿童设计,也不主动采集年龄。如果你是监护人并认为孩子在这里留下了数据,按第 6 节联系我们删除。
这份政策变更时会改动页首的「最后更新」日期。涉及采集范围扩大的变更,会在应用内明示后再生效,不会静默改。
隐私相关的问题、导出协助、删除请求,都发到 support@{{DOMAIN}}。运营者:{{OPERATOR}}。
Time & Effort Check-in · Operated by {{OPERATOR}} · Last updated 2026-09-13
This page describes only what the product actually does. Every statement maps to code; the file path is in brackets so you can check it yourself.
Every core feature — setting a reassessment point, reassessing right now, deciding, notes, history, review, export — works without signing up and without a network connection. While signed out, your data lives entirely in this device's browser localStorage (key sl.v2) and never reaches our servers (public/app/storage.js).
Browser localStorage is isolated by site and device: another browser on the same machine cannot see it, and neither can anyone else's device.
Only if you choose to register and sign in does your data sync to our storage on Cloudflare Workers KV. The keys and their contents (functions/api/[[route]].js, functions/api/_lib.js):
| Key | Contents | Retention |
|---|---|---|
data:{username} | Your tasks, rounds, decisions, notes, plus the untouched snapshot migrated from older versions | Until you ask us to delete it |
user:{username} | Username and password verifier. Passwords are stored with PBKDF2-SHA256, 30,000 iterations, per-user salt. We do not hold your plaintext password. | Until deletion |
token:{token} | Sign-in session | Expires automatically after 30 days |
tokens:{username} | Reverse index of sessions (max 50), kept only so a password change or recovery can revoke every session | Same as above |
recovery:{username} | Hash of your recovery code (PBKDF2, salted). The plaintext code is never written to disk; it is shown to you once, at generation. | Until regenerated |
entitlement:{username} / ledger:{username} | Pro entitlement state and its change ledger (grants, revocations, refunds), capped at 100 entries | Until deletion |
order:{source}:{id} / redeem:{hash} | Order de-duplication markers and redeem-code usage state | Retained as a financial record |
We do not collect your real name, phone number, location, contacts, device identifiers, or an IP-derived profile. Registration needs a username you invent and a password. No email address is required.
There is no email-binding entry in the interface. Account recovery has exactly one path: the 12-character one-time recovery code shown to you once at registration. The backend keeps an email endpoint, but there is no working outbound mail channel, so we do not surface a capability we cannot deliver (docs/exec/BLOCKERS.md B06).
API rate limiting counts requests in memory, keyed by the CF-Connecting-IP header Cloudflare provides (5 registrations/hour, 10 sign-ins/5 minutes, and so on). That counter is never persisted and disappears when the process restarts. Cloudflare, as our host, retains edge access logs under its own policy; we keep no separate copy.
Product analytics are off by default (analytics: false in public/app/storage.js), and everything works with them off. Only after you switch them on does the app send the following, and nothing else:
session_started, reassessment_opened, decision_confirmed, resume_started, reminder_scheduled, reminder_trigger_observed, upgrade_viewed, purchase_verified, refund_verified.Task names, the conditions and hypotheses you write, notes, feelings, outcome text, audio — not one character is uploaded. This is not a promise but an implementation: functions/api/events/[[path]].js filters field by field against the allowlist, and anything outside it (including any free text) is discarded before storage, taking the whole event with it.
The server also stores no raw events — only an integer counter under agg:{date}:{event}:{platform}, plus a de-duplication marker evdedup:{decisionId} that expires after 7 days. This can tell us "how many decisions were confirmed on a given day"; it cannot be traced back to what any person did.
Turning the switch off stops sending immediately. Counters already incremented cannot be deleted per person, because they never knew who you were.
| Third party | What it receives | Why |
|---|---|---|
| Cloudflare (Pages / Workers KV) | Hosts the pages and API, stores the keys listed in section 2 | It is this product's server |
| Lemon Squeezy (international payments) | Handles your payment details; we never receive your card number. It sends us only the order id, the purchase email and the license key | It is the Merchant of Record for international sales |
| WeChat (in-mini-program payments) | Payment happens inside WeChat's virtual payment; we receive only the WeChat order id and the amount | Platform rules require in-app payment |
There are no other third parties: no advertising, no third-party analytics SDK, no A/B testing platform, no crash reporter. The web client loads no third-party script, font, icon pack or CSS library at all — a hard product constraint (see "design constraints" in README.md).
This site sets no cookies. Your sign-in token and settings live in localStorage, are never attached automatically to third-party requests, and are not used for cross-site tracking. That is why there is no cookie banner here: there is nothing to consent to.
The in-app export writes the complete dataset to a JSON file (named like stop-loss-timer-20260913-142530.json) containing every task, round, decision and note, plus the original snapshot preserved during migration from older versions (buildExport() in public/app/storage.js). It is complete, readable, and not reduced in any way.
Export is offered in settings, in the warning shown as you approach the capacity limit, in the sync-conflict choice, and at sign-in when you choose "cloud only" and thereby discard the local copy — the discarded copy is handed to you as an export rather than silently dropped.
sl.v2.u.{username}) and the token; the cloud copy is untouched. Clearing the browser's site data clears guest data.sl.v1.backup.{timestamp} and the old keys stoploss.v1 written during migration are never deleted automatically — they are your rollback path. Clear site data to remove them.data:, user:, token:, tokens:, recovery:, email:, entitlement: and ledger:, completed and confirmed by reply within 5 business days.Deletion also removes your Pro entitlement record; afterwards the purchase cannot be restored under that account. If you want to keep the entitlement but wipe the content, say so and we will delete only data:.
Order de-duplication markers (order:) and redeem-code usage records (redeem:) survive deletion: they are financial records and contain none of your content.
Everything is served over HTTPS. Passwords are stored with PBKDF2-SHA256 (30,000 iterations, per-user salt) and are unreadable to us. Recovery codes are likewise stored only as hashes. Changing your password or resetting via a recovery code immediately invalidates every existing session on that account.
We make no "unbreakable" claim. What we can say: no plaintext password, no plaintext recovery code, and no personal information we do not need is stored on our servers.
This product is not designed for children under 14 and does not collect age. If you are a guardian and believe your child left data here, contact us as described in section 6 and we will delete it.
Any change updates the "Last updated" date at the top. Changes that widen what we collect will be shown in the app before they take effect; they will not happen silently.
Privacy questions, export help and deletion requests all go to support@{{DOMAIN}}. Operator: {{OPERATOR}}.