diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..2e669b8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{py,pyi}] +indent_size = 4 + +[*.{rs,toml}] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[Makefile] +indent_style = tab diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..4e7b9bc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,110 @@ +# Auto-detect text files and normalize line endings to LF on commit. +* text=auto eol=lf + +# Shell scripts must keep LF. +*.sh text eol=lf +*.bash text eol=lf +*.zsh text eol=lf + +# Windows scripts must keep CRLF. +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf + +# Python: enforce LF. +*.py text eol=lf +*.pyi text eol=lf + +# JS/TS/Web: enforce LF. +*.js text eol=lf +*.cjs text eol=lf +*.mjs text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.jsx text eol=lf +*.vue text eol=lf +*.svelte text eol=lf +*.html text eol=lf +*.css text eol=lf +*.scss text eol=lf +*.less text eol=lf +*.json text eol=lf +*.json5 text eol=lf +*.jsonc text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.toml text eol=lf +*.md text eol=lf +*.mdx text eol=lf +*.xml text eol=lf +*.svg text eol=lf + +# Rust: enforce LF. +*.rs text eol=lf + +# C/C++/Go/Java/Kotlin/Swift: enforce LF. +*.c text eol=lf +*.cc text eol=lf +*.cpp text eol=lf +*.cxx text eol=lf +*.h text eol=lf +*.hpp text eol=lf +*.go text eol=lf +*.java text eol=lf +*.kt text eol=lf +*.kts text eol=lf +*.swift text eol=lf + +# Lockfiles: keep as-is, no normalization. +*.lock -text +pnpm-lock.yaml -text +package-lock.json -text +yarn.lock -text +Cargo.lock -text +poetry.lock -text +uv.lock -text +composer.lock -text +Gemfile.lock -text + +# Binary files: do not diff. +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.webp binary +*.ico binary +*.pdf binary +*.zip binary +*.tar binary +*.gz binary +*.tgz binary +*.7z binary +*.rar binary +*.woff binary +*.woff2 binary +*.eot binary +*.ttf binary +*.otf binary +*.mp4 binary +*.webm binary +*.mov binary +*.mp3 binary +*.wav binary +*.flac binary +*.ogg binary + +# Linguist overrides: mark vendored / generated / docs correctly. +docs/* linguist-documentation +CHANGELOG.md linguist-documentation +CONTRIBUTING.md linguist-documentation +CODE_OF_CONDUCT.md linguist-documentation +SECURITY.md linguist-documentation +LICENSE linguist-vendored +.github/ linguist-documentation +*.min.js linguist-generated +*.min.css linguist-generated +*_pb2.py linguist-generated +*_pb2_grpc.py linguist-generated +/dist/ linguist-generated +/build/ linguist-generated +/node_modules/ linguist-vendored diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..bbbc974 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,12 @@ +# Code owners: auto-assign reviewers for pull requests. +# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +# Default owner for everything in the repo. +* @badhope + +# Security-sensitive files get an explicit owner reminder. +/SECURITY.md @badhope +/.github/workflows/ @badhope +/.gitleaks.toml @badhope +/.github/dependabot.yml @badhope +/CODEOWNERS @badhope diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..74bae86 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,89 @@ +name: Bug Report +description: Something is broken or behaving wrong. +title: "[bug]: " +labels: ["bug", "triage"] +assignees: [] + +body: + - type: markdown + attributes: + value: | + Thanks for taking the time. The more detail, the faster I can + reproduce and fix this. + + - type: textarea + id: what + attributes: + label: What happened? + description: Clear, short description of the bug. + placeholder: "When I run `...`, I get `...` instead of `...`." + validations: + required: true + + - type: textarea + id: repro + attributes: + label: Steps to reproduce + description: Numbered. Smallest example you can manage. + placeholder: | + 1. Install with `...` + 2. Run `...` + 3. Open `...` + validations: + required: true + + - type: textarea + id: expected + attributes: + label: Expected + validations: + required: true + + - type: textarea + id: actual + attributes: + label: Actual (include logs / stack traces) + validations: + required: true + + - type: input + id: version + attributes: + label: Version / commit + placeholder: "v1.2.3 or abcdef0" + validations: + required: true + + - type: dropdown + id: where + attributes: + label: Where did you run it? + options: + - Local (macOS) + - Local (Linux) + - Local (Windows) + - Docker / container + - CI + - Cloud / VM + - Other + validations: + required: false + + - type: dropdown + id: severity + attributes: + label: How bad? + options: + - Low (cosmetic / minor inconvenience) + - Medium (broken feature, workaround exists) + - High (broken feature, no workaround) + - Critical (data loss, security, outage) + validations: + required: false + + - type: textarea + id: context + attributes: + label: Anything else + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..d9ad401 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,55 @@ +name: Feature Request +description: Suggest a new feature or improvement. +title: "[feat]: " +labels: ["enhancement", "triage"] +assignees: [] + +body: + - type: markdown + attributes: + value: | + Describe the **problem** first, then the proposed **solution**. + The "why" matters more than the "how". + + - type: textarea + id: problem + attributes: + label: The problem + description: What's painful? What can't you do today? + placeholder: "Right now I have to ...; this is annoying because ..." + validations: + required: true + + - type: textarea + id: solution + attributes: + label: What I want + description: The feature, change, or improvement. + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: What I tried / considered + validations: + required: false + + - type: textarea + id: scope + attributes: + label: Scope + description: Does this change user-facing behavior, the API, the data model, or the build? Any backward-compat risk? + validations: + required: false + + - type: dropdown + id: willingness + attributes: + label: My willingness + options: + - I'd send a PR for this + - I can help test / review + - I can only describe the use case + validations: + required: false diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 06397e2..3834887 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,60 +1,27 @@ -# Pull Request +## What -感谢你为 TaskFlow 做出贡献!🎉 + -## 📋 描述 +## Why -清晰、简洁地描述这个 PR 做了什么。 + -## 🔗 关联 Issue +## How -- 关闭 / 关联: #issue_number -- 关联 Discussion: #discussion_number (如有) + -## 🧪 测试 +## Tested -描述你如何测试这些更改: + -- [ ] 单元测试通过 (`npm test`,如有) -- [ ] 手动测试 Web (`npm run web`) -- [ ] 手动测试 Android (`npm run android`) -- [ ] 手动测试 iOS (`npm run ios`) -- [ ] TypeScript 校验通过 (`npm run typecheck`) +## Checklist -## 📸 截图 / 录屏 +- [ ] I have read [`CONTRIBUTING.md`](./CONTRIBUTING.md) +- [ ] Tests pass locally (and CI is green) +- [ ] I didn't add any new linter / typecheck warnings +- [ ] I didn't commit any secrets, build output, or large binaries +- [ ] I added a `CHANGELOG.md` entry under "Unreleased" if it matters -如果 PR 包含 UI 改动,请附上截图或录屏。 +## Screenshots / Logs -### Before -(改动前) - -### After -(改动后) - -## 📋 改动类型 - -请勾选适用的选项: - -- [ ] 🐛 Bug fix (非破坏性修复) -- [ ] ✨ New feature (非破坏性新功能) -- [ ] 💥 Breaking change (会破坏现有功能的修改) -- [ ] 📝 Documentation update -- [ ] 🎨 Style/UI (无功能影响) -- [ ] ♻️ Refactor (无功能影响的代码重构) -- [ ] ⚡ Performance improvement -- [ ] ✅ Test (添加或修改测试) -- [ ] 🔧 Build/CI (构建或 CI 改动) - -## 📋 清单 - -- [ ] 我的代码遵循项目的代码风格 -- [ ] 我已经自我审查了代码 -- [ ] 我在必要的部分添加了注释(特别是难以理解的代码) -- [ ] 我更新了相关文档 -- [ ] 我的改动没有产生新的警告 -- [ ] 我添加了测试来证明我的修复/功能有效 -- [ ] 新的和现有的单元测试都通过 - -## 📋 额外说明 - -任何需要审查者特别关注的地方、设计决策、权衡等。 + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..29759ef --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,42 @@ +version: 2 + +version: 2 + +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + commit-message: + prefix: deps + include: scope + groups: + production-dependencies: + dependency-type: production + development-dependencies: + dependency-type: development + + +version: 2 + +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + day: monday + open-pull-requests-limit: 5 + labels: + - dependencies + commit-message: + prefix: deps + include: scope + groups: + production-dependencies: + dependency-type: production + development-dependencies: + dependency-type: development diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ac23c89 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,163 @@ +name: CI + +on: + push: + branches: [main, master, develop] + pull_request: + branches: [main, master, develop] + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Node + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20" + + - name: Detect package manager + id: pm + run: | + if [ -f pnpm-lock.yaml ]; then echo "pm=pnpm" >> "$GITHUB_OUTPUT" + elif [ -f yarn.lock ]; then echo "pm=yarn" >> "$GITHUB_OUTPUT" + elif [ -f bun.lockb ] || [ -f bun.lock ]; then echo "pm=bun" >> "$GITHUB_OUTPUT" + else echo "pm=npm" >> "$GITHUB_OUTPUT"; fi + + - name: Install pnpm + if: steps.pm.outputs.pm == 'pnpm' + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.1.0 + with: + version: 9 + + - name: Cache deps + uses: actions/cache@v4 + with: + path: | + node_modules + ~/.pnpm-store + ~/.cache + key: ${{ runner.os }}-${{ steps.pm.outputs.pm }}-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock', '**/bun.lock', '**/bun.lockb') }} + restore-keys: | + ${{ runner.os }}-${{ steps.pm.outputs.pm }}- + + - name: Install + run: | + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm install --frozen-lockfile ;; + yarn) yarn install --frozen-lockfile ;; + bun) bun install --frozen-lockfile ;; + npm) npm ci ;; + esac + + - name: Lint + run: | + if grep -q '"lint"' package.json; then + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm run lint ;; + yarn) yarn lint ;; + bun) bun run lint ;; + npm) npm run lint -- + esac + fi + + - name: Format check + run: | + if grep -q '"format:check"\|"format"' package.json; then + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm run format:check || pnpm run format -- --check ;; + yarn) yarn format:check || yarn format --check ;; + bun) bun run format:check || bun run format --check ;; + npm) npm run format:check -- --if-present || npm run format -- --check + esac + fi + + test: + name: Test / Build + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Harden Runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: Checkout + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Set up Node + uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "20" + + - name: Detect package manager + id: pm + run: | + if [ -f pnpm-lock.yaml ]; then echo "pm=pnpm" >> "$GITHUB_OUTPUT" + elif [ -f yarn.lock ]; then echo "pm=yarn" >> "$GITHUB_OUTPUT" + elif [ -f bun.lockb ] || [ -f bun.lock ]; then echo "pm=bun" >> "$GITHUB_OUTPUT" + else echo "pm=npm" >> "$GITHUB_OUTPUT"; fi + + - name: Install pnpm + if: steps.pm.outputs.pm == 'pnpm' + uses: pnpm/action-setup@fe02b34f77f8bc703788d5817da081398fad5dd2 # v4.1.0 + with: + version: 9 + + - name: Cache deps + uses: actions/cache@v4 + with: + path: | + node_modules + ~/.pnpm-store + ~/.cache + key: ${{ runner.os }}-${{ steps.pm.outputs.pm }}-${{ hashFiles('**/package-lock.json', '**/pnpm-lock.yaml', '**/yarn.lock', '**/bun.lock', '**/bun.lockb') }} + restore-keys: | + ${{ runner.os }}-${{ steps.pm.outputs.pm }}- + + - name: Install + run: | + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm install --frozen-lockfile ;; + yarn) yarn install --frozen-lockfile ;; + bun) bun install --frozen-lockfile ;; + npm) npm ci ;; + esac + + - name: Test + run: | + if grep -q '"test"' package.json; then + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm test -- --run ;; + yarn) yarn test --run ;; + bun) bun test ;; + npm) npm test -- --watch=false + esac + fi + + - name: Build + run: | + if grep -q '"build"' package.json; then + case "${{ steps.pm.outputs.pm }}" in + pnpm) pnpm run build ;; + yarn) yarn build ;; + bun) bun run build ;; + npm) npm run build -- + esac + fi diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..c2e7774 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,44 @@ +# gitleaks custom config for badhope repos. +# Keeps the default ruleset and adds allowlists for known false-positive +# categories (tests, docs, samples, placeholders) so CI doesn't drown in +# noise that masks real findings. + +title = "badhope gitleaks config" + +[allowlist] +description = "Global allowlist for known false-positive categories" + +# Test files, fixtures, snapshots, and generated test data +paths = [ + '''(^|/)tests?/.*''', + '''(^|/)__tests__/.*''', + '''(^|/)spec/.*\.md$''', + '''(^|/)testdata/.*''', + '''(^|/)fixtures?/.*''', + '''(^|/)mocks?/.*''', + '''.*_test\.go$''', + '''.*\.test\.(ts|js|tsx|jsx|py)$''', + '''.*_test\.(py|ts|js)$''', + '''.*test_[^/]*\.py$''', + '''.*_fixture\.(json|ya?ml|toml)$''', + '''.*\.spec\.(ts|js|tsx|jsx)$''', +] + +# Documentation and example files (curl examples, README snippets) +paths2 = [ + '''(^|/)docs?/.*\.(md|mdx|rst|txt)$''', + '''(^|/)README\.(md|rst|txt)$''', + '''(^|/)CHANGELOG\.(md|rst|txt)$''', + '''(^|/)CONTRIBUTING\.(md|rst|txt)$''', + '''(^|/)SECURITY\.(md|rst|txt)$''', + '''(^|/)\.github/.*\.(md|mdx)$''', + '''.*\.example$''', + '''.*\.sample$''', + '''.*\.template$''', +] + +# Environment variable templates are USUALLY placeholders +regexes = [ + '''(?i)(api[_-]?key|secret|token|password|passwd|access[_-]?key)\s*[:=]\s*['"]?(CHANGE_?ME|YOUR_|EXAMPLE_|PLACEHOLDER|XXXX|REDACTED|TEST|FAKE|DUMMY|<|<%|<<|<<%)''', + '''(?i)(api[_-]?key|secret|token|password)\s*[:=]\s*\$\{?[A-Z_][A-Z0-9_]*\}?''', +] diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..1298f65 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,29 @@ +cff-version: 1.2.0 +message: >- + If you use this software in academic work, please cite it using the + metadata below. +title: TaskFlow +authors: + - family-names: badhope + given-names: badhope + alias: badhope +identifiers: + - type: url + value: https://github.com/badhope/TaskFlow +license: MIT +type: software +url: https://github.com/badhope/TaskFlow +repository-code: https://github.com/badhope/TaskFlow +keywords: + - ai-suggestions + - android + - cross-platform + - drag-and-drop + - expo + - focus-mode + - gantt + - ios + - kanban + - material-icons +abstract: >- + TaskFlow — my cross-platform todo app (Web / Android / iOS). Pomodoro, AI suggestions (statistical, not magic), drag-and-drop, 6 views, 13 themes. Strict TypeScript end to end. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 30097bd..225e09f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,51 +1,10 @@ # Code of Conduct -This is the standard [Contributor Covenant](https://www.contributor-covenant.org/), -version 2.1, lightly adapted. The original is fine; I haven't -rewritten the substance. +Be a grown-up. Disagreements are fine; disrespect isn't. -## Our pledge +If someone's being awful, open a private security advisory on this repo +and I'll handle it. If it's me being awful, you can email me via the +address in `CITATION.cff` (or my profile page, if there isn't one). -We as members, contributors, and leaders pledge to make participation -in our community a harassment-free experience for everyone, regardless -of age, body size, visible or invisible disability, ethnicity, sex -characteristics, gender identity and expression, level of experience, -education, socio-economic status, nationality, personal appearance, -race, caste, color, religion, or sexual identity and orientation. - -## Our standards - -Examples of behaviour that contributes to a positive environment: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our - mistakes, and learning from the experience -- Focusing on what is best not just for us as individuals, but for - the overall community - -Examples of unacceptable behaviour: - -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or - political attacks -- Public or private harassment -- Publishing others' private information without their explicit - permission -- Other conduct which could reasonably be considered inappropriate - in a professional setting - -## Enforcement - -Report unacceptable behaviour via GitHub Issues with the `conduct` -label, or by contacting the maintainer privately (see their GitHub -profile for an email — I don't put it in plaintext to keep scrapers -away). - -All complaints will be reviewed and investigated promptly and fairly. - -## Attribution - -Adapted from the [Contributor Covenant, v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct.html). +Adapted — and shortened — from the +[Contributor Covenant, v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/). diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ad401a..9f20dec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,80 +1,46 @@ # Contributing -I work on this in my spare time and welcome outside help. +Want to send a PR? Cool. Here's the flow. -## What I need +## Before you start -- Bug reports with a minimal reproduction (a screen recording goes a - long way). -- Pull requests for clear bugs. For new features, open an issue first - so we can argue about the design before you spend a weekend on it. -- Documentation fixes are always welcome. Typos, broken links, missing - context — send them. +- **Open an issue first** if the change is non-trivial. I don't want you + to spend a weekend on something I'd have asked you to do differently. +- Read the code that's already there. I try to keep it boring and + consistent. If you're adding a new pattern, it should fit. +- Look at the README for how to install / run / test. Use the lockfile + that's checked in; don't regenerate it. -## What I don't need +## Local checks -- Rewrites in a different framework. -- Lint wars. If you find a real bug, fix it; don't open a PR with 47 - formatting changes. -- Drive-by refactors of files you touched for a one-line fix. - -## Local setup - -```bash -git clone https://github.com/badhope/TaskFlow -cd TaskFlow -npm install -npm run web # fastest path — http://localhost:8081 -npm run typecheck # should be 0 errors -npm run lint # should be 0 errors (warnings OK) -``` - -Node 18 or later. I develop on whatever the current LTS is, so older -versions are not tested. +Run whatever the project has: `pnpm test`, `pytest`, `cargo test`, etc. +If linter / formatter configs are checked in, run them too. CI will +catch what you missed, but a green push is faster than a red one. ## Commit messages -I don't enforce conventional commits. Write whatever you'd write if -nobody was watching. Some recent ones for the style: - -- `polish: a11y, error boundary, and a less-AI-feeling README` -- `cache-buster was corrupting URLs in `src/` paths; switched to python` -- `add tasks with the same title at the same minute shouldn't crash` - -No more than one short subject line, blank line, then a paragraph or -two if the diff is non-obvious. If the PR fixes an issue, the issue -number at the end is fine. - -## Code style - -TypeScript strict mode is on. The lint config is in `eslint.config.js`; -the rules are pragmatic, not the React/recommended set with every -warning turned into an error. If you want to add a rule, justify it -in the PR. - -Components that render in a list should be wrapped in `React.memo`. -The rest is judgement. +I don't enforce Conventional Commits. Subject, blank line, body, done. +If a commit fixes an issue, mention the issue number. Don't bother with +emoji or "WIP" prefixes. -## Where to put things +## Pull requests -| New... | Goes in | -|--------------------------|------------------------------------------| -| Route / page | `screens/` | -| Generic UI primitive | `src/shared/components/common/` | -| New non-list view | `src/shared/components/views/` | -| Stateful reusable logic | `src/shared/hooks/` | -| New persisted shape | `src/shared/types/index.ts` + the store | -| Theme color / token | both light + dark presets in the store | +- Fill the PR template. One paragraph in the body is fine; screenshots + help for UI. +- Keep the diff small. Squash before merging unless the history matters. +- I'll review roughly in order of arrival. If CI is green and the change + does what the description says, I'll merge. I might push back on + architecture; that's not personal. +- Don't commit secrets, generated build output, large binaries, or + someone else's code without a license. -## Pull request process +## What I won't merge -1. Branch from `main`. Name it whatever, I don't care. -2. Run `npm run typecheck && npm run lint` before pushing. -3. In the PR description, link the issue (if any) and a one-line - summary of what changed and why. -4. Be patient. I'll get to it. +- Drive-by refactors that don't fix a real problem. +- New dependencies for trivial reasons. +- Anything that breaks the existing API without a heads-up first. -## Code of conduct +## License -[CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) — short version: be a decent -human. +By contributing, you agree your contribution is licensed under the same +license as the rest of the project. See [`LICENSE`](./LICENSE). diff --git a/SECURITY.md b/SECURITY.md index f3dfa95..5d907da 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,66 +1,35 @@ # Security -This project is a local-first single-user app, so the threat model is -mostly "don't accidentally leak the user's tasks to a third party". -Most of the below is already done; the rest is documented for honesty. +Found a hole? **Don't file a public issue.** Open a private security +advisory or email me (the address is in `CITATION.cff` if there's one; +otherwise it's on my profile page). I prefer the advisory form because +GitHub handles the disclosure timeline. -## Reporting a vulnerability +What I'll do: -Please don't open a public GitHub Issue for security stuff. Use one -of: +- Reply within 3 business days. +- Triage and try to reproduce within 10 business days. +- Ship a fix, or at least a documented mitigation, as soon as I can. +- Credit you in the advisory if you want it. Say "anonymous" if you don't. -- **GitHub Security Advisories** (preferred): the "Security" tab on - the repo, then "Advisories" → "New draft security advisory". This - gives us a private channel and lets us coordinate a fix before - public disclosure. -- **Email**: see the GitHub profile of the maintainer for a current - address. (I won't put it in plaintext here to keep scrapers away.) +I follow responsible disclosure: please keep the report private until I +publish a fix and (if needed) a CVE / advisory. I won't sue you for +security research done in good faith, and I won't go after security +researchers for things that are obviously bugs. -Please include: -- What you found and how to reproduce it. -- The version affected. -- Your assessment of impact. -- Whether you'd like to be credited when we ship the fix. +## What I patch -I try to acknowledge within a few days. Realistic fix timeline depends -on severity, but a critical issue gets a patch in days, not weeks. +Only the latest commit on `main`. I don't backport. If you're on an +older version, the right fix is to upgrade. -## Current posture +## In scope -**Data storage.** All persisted data lives in AsyncStorage on the -device. No server, no telemetry, no third-party SDKs that phone home. -On web, that's `localStorage`; on mobile, the platform-native -AsyncStorage. +- Code in this repository. +- Official container images and release artifacts that came from this + repo (when they exist). -**Network.** The app does not initiate outbound requests in normal -operation. The web bundle makes no XHR/fetch calls. Expo's dev-time -hot-reload websocket is the only network traffic in development. +## Out of scope -**No auth.** No login, no tokens, no account. There is nothing to -phish. - -**Dependencies.** `npm audit` is run regularly. The known Expo SDK 50 -transitive vulnerabilities (mostly `tar`, `uuid`, `cacache`) are -documented in the README's "Known caveats" — fixing them requires -upgrading to Expo SDK 56, which is a breaking change tracked -separately. - -**No `dangerouslySetInnerHTML`, no `eval`, no `new Function`.** I -checked. The codebase doesn't render user-supplied HTML or evaluate -dynamic code anywhere. - -## Things you should know as a user - -- Clearing browser/site data will delete the local store. **Export - first** (Settings → Data → Export) if you care about it. -- The web bundle is served over whatever HTTPS your hosting provider - gives you. GitHub Pages, Netlify, and Vercel all default to HTTPS. -- Don't run the dev server on a network you don't trust. Metro's - dev-time websocket has no auth. - -## Scope - -This policy covers the source in this repository. It does not cover -the Expo runtime, the React Native runtime, or any host platform's -own security guarantees. Those are the upstream projects' -responsibility. +- Third-party dependencies. Report upstream unless I pinned and shipped + a vulnerable version myself. +- Scanners, social engineering, DoS, or "you used a default port".