{"id":16267032,"url":"https://github.com/wezm/leaf","last_synced_at":"2025-03-19T23:30:48.912Z","repository":{"id":37177628,"uuid":"239441803","full_name":"wezm/leaf","owner":"wezm","description":"Lightweight, self-hosted task tracking","archived":false,"fork":false,"pushed_at":"2023-04-03T21:54:25.000Z","size":396,"stargazers_count":49,"open_issues_count":2,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-28T21:32:58.907Z","etag":null,"topics":["plaintext","rust","task-list","todo"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wezm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-10T06:18:45.000Z","updated_at":"2025-02-07T10:45:31.000Z","dependencies_parsed_at":"2024-10-27T21:38:20.660Z","dependency_job_id":"bfd2db0e-79a7-4eeb-9ebf-5b95efe7422f","html_url":"https://github.com/wezm/leaf","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wezm%2Fleaf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wezm%2Fleaf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wezm%2Fleaf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wezm%2Fleaf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wezm","download_url":"https://codeload.github.com/wezm/leaf/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244030752,"owners_count":20386533,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["plaintext","rust","task-list","todo"],"created_at":"2024-10-10T17:43:40.169Z","updated_at":"2025-03-19T23:30:48.414Z","avatar_url":"https://github.com/wezm.png","language":"Rust","readme":"🍃 Leaf Tasks\n=============\n\nLeaf is a lightweight, web based, self-hosted task tracking (todo) tool.\n\nI created Leaf Tasks as replacement for my somewhat specific use of\nWunderlist. I curate, [Read Rust], a site that collects interesting\nposts from the Rust community. My workflow for the site is mostly\npowered by RSS and [Feedbin] but when I encounter a post outside of\nFeedbin — typically on my phone I use the iOS share sheet functionality\nto add a link to Wunderlist. With Wunderlist being shut down in May\n2020 I built Leaf as a replacement.\n\n\u003cimg src=\"https://github.com/wezm/leaf/raw/master/screenshot.png\" width=\"698\"\u003e\n\nFeatures\n--------\n\nWhat's included:\n\n* A simple task list that lets you add and complete tasks.\n* Uncluttered design.\n* Plain text (CSV) storage.\n* Uses plain old HTML forms — works in almost any browser, including [Lynx]\n  ([Screenshot][lynx-screenshot]).\n* Single file, dependency-free binary.\n* Super fast — Typical response times are ~160µs.\n* Memory efficient — Uses ~1.4Mb RAM.\n\nWhat's not included:\n\n* JavaScript.\n* User tracking.\n* Multiple lists.\n* Multiple users.\n* Sharing (outside of sharing a login).\n* Task editing.\n* Task deletion.\n* Viewing completed tasks in the UI (although they are stored in a file).\n\nDownload\n--------\n\nPre-built binaries are available:\n\n* [FreeBSD 13 amd64](https://releases.wezm.net/leaf/0.4.0/leaf-0.4.0-amd64-unknown-freebsd.tar.gz)\n* [Linux x86\\_64](https://releases.wezm.net/leaf/0.4.0/leaf-0.4.0-x86_64-unknown-linux-musl.tar.gz)\n* [Mac OS](https://releases.wezm.net/leaf/0.4.0/leaf-0.4.0-x86_64-apple-darwin.tar.gz)\n* [Windows x86\\_64](https://releases.wezm.net/leaf/0.4.0/leaf-0.4.0-x86_64-pc-windows-msvc.zip)\n\nUsing\n-----\n\n### Shortcuts Workflow for iOS\n\nThis workflow for the built-in Shortcuts app allows you to add new tasks using the\nstandard share sheet.\n\n\u003chttps://www.icloud.com/shortcuts/aabb57aa14a2465a82002c83a33bb0e6\u003e\n\nYou will need to customise two things:\n\n1. In the Text block with \"Bearer `your-api-token`\", replace `your-api-token`\n   with the token the Leaf instance is using (`LEAF_API_TOKEN` environment\n   variable).\n2. In the URL block, replace https://example.com/tasks with the URL of your\n   Leaf instance.\n\n### Tips\n\n* There's no need to click the Save button when adding a task. Just hit Enter\n  and the default browser behaviour of submitting the form will take place.\n\n### Font\n\nTo minimise page weight Leaf does not use any web fonts. However it was\ndesigned using the [Muli font][Muli] and this font is specified in the CSS.\nInstall the font if you would like Leaf use it. If you'd rather not install it,\nthat's fine — Leaf will use your browsers default sans-serif font.\n\nFAQ\n---\n\n### Why no editing or deletion of tasks?\n\nJust complete it and add a new one.\n\n### Why no completed task list?\n\nLeaf stores all completed tasks in a separate file for manual review if needed.\nTo avoid unnecessarily complicating the UI it is not exposed there though.\n\n### What if I accidentally complete a task?\n\nAdd it again as a new task. If you're unsure of the content review the completed\ntask list file manually.\n\n### What if I really want multiple lists?\n\nYou can run multiple instances of Leaf. Each server process is very small.\n\nRunning\n-------\n\n### Configuration\n\nLeaf uses environment variables for configuration.\n\n#### `LEAF_PASSWORD_HASH`\n\nThis contains the password hash used to verify you when logging in. The value\ncan be generated with the `argon2` tool. This tool is installed by default on\nArch Linux. If you are using a different system you may need to install it, the\npackage is probably called `argon2`.\n\nThe shell snippet below will read your password from stdin and then print the\nhash. Type your chosen password and press Enter, note that it will echo in the\nterminal. See below for an\n[explanation of the snippet](#password-hash-shell-snippet-explanation).\n\n    (read -r PASS; echo -n \"$PASS\" | argon2 $(cat /dev/urandom | LC_ALL=C tr -dc 'a-zA-Z0-9' | head -c 8) -e)\n\nYou should see something like the following, which is what `LEAF_PASSWORD_HASH`\nshould be set to.\n\n    $argon2i$v=19$m=4096,t=3,p=1$eEVkYlJFZGY$N0p7VxqHDGBZ1ivgotGv2olZ/eXM9WPPCRf0wZuyyLo\n\n**Note:** The hash contains `$` characters so be aware of shell quoting issues.\nIf setting the var in a shell use single quotes:\n\n    export LEAF_PASSWORD_HASH='$argon2i$v=19$m=4096,t=3,p=1$eEVkYlJFZGY$N0p7VxqHDGBZ1ivgotGv2olZ/eXM9WPPCRf0wZuyyLo'\n\n#### `LEAF_API_TOKEN`\n\nThe contents of this environment variable is used as a Bearer token (password)\nfor the add task route. I use it to add tasks on my phone with the iOS\nShortcuts workflow above. It must be at least 64 characters long. I used my\n[password manager][gopass] to generate mine.\n\n    export LEAF_API_TOKEN=Insert64orMoreRandomCharactersHere\n\n#### `ROCKET_SECRET_KEY`\n\nThis is used to encrypt the cookie used for authentication. I can be generated with:\n\n    openssl rand -base64 32\n\n#### `LEAF_TASKS_PATH` (optional)\n\n**Default:** `tasks.csv` in the working directory.\n\nThe path to the CSV file that will store tasks. If it does not exist it will be\ncreated.\n\n**Note:** `~` is not expanded in environment variables. If you want to refer to\nyour home directory use `$HOME`. E.g.\n`LEAF_TASKS_PATH=$HOME/Documents/tasks.csv`.\n\n#### `LEAF_COMPLETED_PATH` (optional)\n\n**Default:** `completed.csv` in the working directory.\n\nThe path to the CSV file that will store completed tasks. If it does not exist\nit will be created.\n\n**Note:** `~` is not expanded in environment variables. If you want to refer to\nyour home directory use `$HOME`. E.g.\n`LEAF_COMPLETED_PATH=$HOME/Documents/completed.csv`.\n\n#### `LEAF_SECURE_COOKIE` (optional)\n\n**Default:** `true`\n\nWhether the login cookie sets [the secure flag][secure-cookie]. For local development\nwithout https, set this to `false.`\n\n#### Rocket Configuration\n\nThe web framework Leaf uses ([Rocket]), also has some of its own configuration\noptions: \u003chttps://rocket.rs/v0.4/guide/configuration/\u003e.\n\nFile Format\n-----------\n\nTODO\n\nAPI\n---\n\nTODO\n\nDevelopment\n-----------\n\n[![Build Status](https://api.cirrus-ci.com/github/wezm/leaf.svg)](https://cirrus-ci.com/github/wezm/leaf)\n\n### Auto-reloading server\n\nTo run the server during development and have it rebuild and restart when\nsource files are changed I use [watchexec]:\n\n    watchexec -w src -s SIGINT -r 'cargo run'\n\n### Linking with lld\n\nUsing `lld` speeds up linking. I see 0.71s vs. 1.76s for an incremental build,\n15 vs. 19s for clean build. Add the following to `.cargo/config`:\n\n```toml\n[target.x86_64-unknown-linux-gnu]\nrustflags = [\n    \"-C\", \"link-arg=-fuse-ld=lld\",\n]\n```\n\nLicence\n-------\n\nThis project is dual licenced under either of:\n\n- Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/wezm/leaf/blob/master/LICENSE-APACHE))\n- MIT license ([LICENSE-MIT](https://github.com/wezm/leaf/blob/master/LICENSE-MIT))\n\nat your option.\n\nAppendix\n--------\n\n### Password Hash Shell Snippet Explanation\n\n* The outer brackets `()` run the command in a sub-shell, this is to prevent\n  the `PASS` environment variable remaining set in your shell.\n* `read -r PASS` reads a line from stdin and sets the `PASS` environment\n  variable with the value read, minus the terminalting new-line. `-r` disables\n  `\\` escape sequence support.\n* `echo -n \"$PASS\"` prints the password on stdout,  `-n` disables the trailing\n  new-line.\n* `read` + `echo` are used to avoid having the password in your shell history,\n  if it has one.\n* `$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c 8)` is used to generate a\n  random \"salt\" for the hash.\n  * `$()` runs a command and substitutes it with the output from that command.\n  * `cat /dev/urandom` reads from the `dev/urandom` pseudo random number\n    generator device and writes to stdout.\n  * `tr -dc 'a-zA-Z0-9'` reads from stdin and drops (`-d`) characters not in\n    the set supplied to `-c`. This has the effect of filtering the binary\n    `/dev/urandom` data and only outputting characters 'a-zA-Z0-9'.\n  * `head -c 8` reads the first 8 characters (`-c`) from stdin and outputs them\n    on stdout.\n* `argon2` receives the random salt as an argument, it reads the password from\n  stdin and prints just the encoded hash on stdout (`-e`).\n\n[Read Rust]: https://readrust.net/\n[Feedbin]: https://feedbin.com/\n[watchexec]: https://github.com/watchexec/watchexec\n[Lynx]: https://lynx.invisible-island.net/\n[Muli]: https://www.fontsquirrel.com/fonts/muli\n[secure-cookie]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Secure\n[gopass]: https://www.gopass.pw/\n[Rocket]: https://rocket.rs/\n[lynx-screenshot]: https://github.com/wezm/leaf/blob/master/screenshot-lynx.png\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwezm%2Fleaf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwezm%2Fleaf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwezm%2Fleaf/lists"}