{"id":48487328,"url":"https://github.com/knirski/paperless-ingestion-bot","last_synced_at":"2026-04-07T10:03:56.274Z","repository":{"id":342743079,"uuid":"1174916737","full_name":"knirski/paperless-ingestion-bot","owner":"knirski","description":"Privacy-first document ingestion for Paperless-ngx supercharged with local AI","archived":false,"fork":false,"pushed_at":"2026-04-05T02:51:35.000Z","size":698,"stargazers_count":0,"open_issues_count":20,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T04:27:00.139Z","etag":null,"topics":["cli","document-ingestion","effect","gmail","imap","nodejs","paperless-ngx","signal","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/knirski.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":"CITATION.cff","codeowners":"CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"knirski","liberapay":"knirski"}},"created_at":"2026-03-07T01:40:11.000Z","updated_at":"2026-03-22T07:52:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/knirski/paperless-ingestion-bot","commit_stats":null,"previous_names":["knirski/paperless-ingestion-bot"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/knirski/paperless-ingestion-bot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knirski%2Fpaperless-ingestion-bot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knirski%2Fpaperless-ingestion-bot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knirski%2Fpaperless-ingestion-bot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knirski%2Fpaperless-ingestion-bot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/knirski","download_url":"https://codeload.github.com/knirski/paperless-ingestion-bot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/knirski%2Fpaperless-ingestion-bot/sbom","scorecard":{"id":1244500,"data":{"date":"2026-03-07T17:46:15Z","repo":{"name":"github.com/knirski/paperless-ingestion-bot","commit":"f9c810d0d1260664c37040c241bbf08180379e38"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":6.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/ci.yml:58","Info: jobLevel 'contents' permission set to 'read': .github/workflows/ci.yml:57","Info: jobLevel 'packages' permission set to 'read': .github/workflows/codeql.yml:37","Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:38","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:39","Warn: jobLevel 'contents' permission set to 'write': .github/workflows/update-nix-hash.yml:15","Info: topLevel 'contents' permission set to 'read': .github/workflows/ci.yml:12","Warn: no topLevel permission defined: .github/workflows/codeql.yml:1","Warn: topLevel 'packages' permission set to 'write': .github/workflows/docker.yml:13","Info: topLevel 'contents' permission set to 'read': .github/workflows/docker.yml:12","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release-please.yml:9","Info: topLevel 'contents' permission set to 'read': .github/workflows/scorecard.yml:11","Warn: topLevel 'security-events' permission set to 'write': .github/workflows/scorecard.yml:12","Warn: no topLevel permission defined: .github/workflows/update-nix-hash.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: SECURITY.md:1","Info: Found linked content: SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: SECURITY.md:1","Info: Found text in security policy: SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"CII-Best-Practices","score":2,"reason":"badge detected: InProgress","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"Pinned-Dependencies","score":8,"reason":"dependency not pinned by hash detected -- score normalized to 8","details":["Warn: containerImage not pinned by hash: Dockerfile:4: pin your Docker image by updating node:24-alpine to node:24-alpine@sha256:7fddd9ddeae8196abf4a3ef2de34e11f7b1a722119f91f28ddf1e99dcafdf114","Warn: containerImage not pinned by hash: Dockerfile:17: pin your Docker image by updating node:24-alpine to node:24-alpine@sha256:7fddd9ddeae8196abf4a3ef2de34e11f7b1a722119f91f28ddf1e99dcafdf114","Info:  16 out of  16 GitHub-owned GitHubAction dependencies pinned","Info:  12 out of  12 third-party GitHubAction dependencies pinned","Info:   0 out of   2 containerImage dependencies pinned","Info:   3 out of   3 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":9,"reason":"SAST tool detected but not run on all commits","details":["Info: SAST configuration detected: CodeQL","Warn: 20 commits out of 22 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: TypeScriptPropertyBasedTesting integration found: test/core.test.ts:4","Info: TypeScriptPropertyBasedTesting integration found: test/fs-utils.test.ts:3","Info: TypeScriptPropertyBasedTesting integration found: test/imap-body-structure.test.ts:1","Info: TypeScriptPropertyBasedTesting integration found: test/runtime.test.ts:2","Info: TypeScriptPropertyBasedTesting integration found: test/core.test.ts:4","Info: TypeScriptPropertyBasedTesting integration found: test/fs-utils.test.ts:3","Info: TypeScriptPropertyBasedTesting integration found: test/imap-body-structure.test.ts:1","Info: TypeScriptPropertyBasedTesting integration found: test/runtime.test.ts:2"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Warn: 'branch protection settings apply to administrators' is disabled on branch 'main'","Info: 'stale review dismissal' is required to merge on branch 'main'","Warn: branch 'main' does not require approvers","Warn: codeowners review is not required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: 'up-to-date branches' is disabled on branch 'main'","Info: status check found to merge onto on branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"Contributors","score":0,"reason":"project has 0 contributing companies or organizations -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"22 out of 22 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}}]},"last_synced_at":"2026-03-07T17:54:57.516Z","repository_id":342743079,"created_at":"2026-03-07T17:54:57.516Z","updated_at":"2026-03-07T17:54:57.516Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31508282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-07T03:10:19.677Z","status":"ssl_error","status_checked_at":"2026-04-07T03:10:13.982Z","response_time":105,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["cli","document-ingestion","effect","gmail","imap","nodejs","paperless-ngx","signal","typescript"],"created_at":"2026-04-07T10:03:55.584Z","updated_at":"2026-04-07T10:03:56.269Z","avatar_url":"https://github.com/knirski.png","language":"TypeScript","funding_links":["https://github.com/sponsors/knirski","https://liberapay.com/knirski","https://liberapay.com/knirski/"],"categories":[],"sub_categories":[],"readme":"# Paperless Ingestion Bot\n\n[![CI](https://github.com/knirski/paperless-ingestion-bot/actions/workflows/ci.yml/badge.svg)](https://github.com/knirski/paperless-ingestion-bot/actions)\n[![Version](https://img.shields.io/github/package-json/v/knirski/paperless-ingestion-bot)](https://github.com/knirski/paperless-ingestion-bot/blob/main/package.json)\n[![Coverage](https://codecov.io/gh/knirski/paperless-ingestion-bot/graph/badge.svg)](https://app.codecov.io/gh/knirski/paperless-ingestion-bot)\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/knirski/paperless-ingestion-bot/badge)](https://scorecard.dev/viewer/?uri=github.com/knirski/paperless-ingestion-bot)\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/license/Apache-2.0)\n[![GitHub Sponsors](https://img.shields.io/badge/GitHub%20Sponsors-Support-ea4aaa.svg)](https://github.com/sponsors/knirski)\n[![Liberapay](https://img.shields.io/badge/Liberapay-Support-yellow.svg)](https://liberapay.com/knirski/)\n[![CII Best Practices](https://img.shields.io/badge/CII%20Best%20Practices-register-green)](https://www.bestpractices.dev/en/projects/new?project_url=https%3A%2F%2Fgithub.com%2Fknirski%2Fpaperless-ingestion-bot)\n\nSignal and Gmail document ingestion for [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx).\n\n**Stop drowning in paper.** Hundreds of emails with receipts, contracts, and attachments buried in your Gmail, or physical docs piling up on your desk? Scan or snap, send via [Signal](https://signal.org/) or email, and let [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx) do the rest. Optional AI (local [Ollama](https://ollama.com/)) filters junk; pair with [paperless-ai](https://github.com/clusterzx/paperless-ai) for tags and summaries. Find any document when you need it.\n\n**Privacy-first:** Runs fully locally. No cloud APIs, no telemetry. Signal is a privacy-focused IM; AI uses local Ollama. Your documents stay on your machine.\n\nSetup is a bit involved. Geeks will feel at home - determined non-geeks can get there too. Linux and macOS, Windows may work (WSL could help).\n\n## Prerequisites\n\n- [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx) (API upload)\n- For **Signal**: [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) must be running first\n- For **Gmail**: Signal must be set up first (accounts are added via Signal commands)\n\n## Features\n\n- **Signal**: Webhook server for document attachments sent via Signal.\n- **Gmail**: IMAP-based crawl for email attachments. One-shot (runs once, exits). Schedule with cron or systemd timer.\n- **Ollama**: Optional AI eligibility filter for email attachments (images, plain text).\n- **Auto-PR**: Push to `ai/**` branches to auto-create PRs with titles from conventional commits via [knirski/auto-pr](https://github.com/knirski/auto-pr).\n\nSupported file types: PDF, Word (.doc, .docx), RTF, Office formats, images (JPEG, PNG, etc.), plain text, HTML, CSV.\n\n### Future\n\nMight add: other email providers (Outlook, Proton), other IMs (Matrix, Telegram), cloud storage, scanners, fax.\n\n### Potentially useful projects\n\nSee [docs/RELATED_PROJECTS.md](docs/RELATED_PROJECTS.md) for a list of projects that complement or extend this bot.\n\n## Dependencies\n\n| Dependency                                                              | Required   | Purpose                                                                                                                                            |\n| ----------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx)         | Yes        | Document management system; bot uploads via REST API                                                                                                 |\n| [Ollama](https://ollama.com/)                                            | No         | Optional AI eligibility assessment; also used for AI-generated PR titles in the auto-PR workflow (runs locally)                                    |\n| [paperless-ai](https://github.com/clusterzx/paperless-ai)               | No         | Optional AI post-processing (tags, titles, correspondents). This bot uses Ollama directly for pre-ingestion; paperless-ai augments after ingestion |\n| [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) | For Signal | Webhook server for Signal Messenger                                                                                                                |\n| Bun ≥ 1.3                                                              | Yes        | Runtime. See [Bun support policy](#bun-support-policy) below.                                                                                         |\n\n## Quick Start\n\nInstall below, then follow [Setup](#setup) (Signal or Gmail) before running commands.\n\n### Installation\n\n**Bun or Nix:**\n\n**Bun (from source):**\n\n```bash\ngit clone https://github.com/knirski/paperless-ingestion-bot.git\ncd paperless-ingestion-bot\nbun install \u0026\u0026 bun run build\n```\n\n**Nix:**\n\n```bash\nnix build .#default          # Build package\nnix develop                  # Development shell\nnix run .#default -- signal --config /path/to/config.json\n```\n\n**Docker:**\n\nImages are published to [GHCR](https://github.com/knirski/paperless-ingestion-bot/pkgs/container/paperless-ingestion-bot) on each release. Release images are signed with Sigstore keyless signing; see [docs/CI.md](docs/CI.md) for verification steps. Use [Compose](deploy/compose/README.md) — minimal (Signal + ingestion bot) or full-stack (Paperless + Signal + Ollama) — or run standalone:\n\n```bash\ndocker run --rm \\\n  -v /path/to/config:/etc/paperless-ingestion-bot:ro \\\n  -v /path/to/data:/var/lib/paperless-ingestion-bot \\\n  ghcr.io/knirski/paperless-ingestion-bot:latest signal\n```\n\nMount your config directory at `/etc/paperless-ingestion-bot` (must contain `config.json`) and a data directory at `/var/lib/paperless-ingestion-bot` (for email-accounts.json, users.json). For Gmail, headless Linux requires a system credential store (libsecret/Secret Service); see [Troubleshooting](#troubleshooting).\n\n**Env overrides:** Override file values with individual env vars (e.g. `-e PAPERLESS_INGESTION_SIGNAL_API_URL=http://signal:8080`). Use `--skip-reachability-check` when the Signal API starts after the bot (e.g. Docker Compose).\n\n**Deployment:** [deploy/](deploy/) — [Compose](deploy/compose/README.md) (minimal or full-stack) and [systemd](deploy/systemd/README.md) (service + timer units).\n\n### Commands\n\n- `paperless-ingestion-bot signal`: Run Signal webhook server (validates `paperless_url` and `signal_api_url` at startup; use `--skip-reachability-check` to bypass API reachability check)\n- `paperless-ingestion-bot email`: Scan Gmail inboxes for attachments (one-shot; use with cron/systemd timer)\n- `paperless-ingestion-bot --help`: Show help\n- `paperless-ingestion-bot --version`: Show version\n- `paperless-ingestion-bot email --json`: Output `{ \"saved\": N }` to stdout for scripting\n- `paperless-ingestion-bot --completions \u003cbash|zsh|fish\u003e`: Print shell completion script (append to `~/.bashrc`, `~/.zshrc`, or `~/.config/fish/config.fish`)\n\n## Setup\n\n### Signal\n\n1. Run [signal-cli-rest-api](https://github.com/bbernhard/signal-cli-rest-api) (e.g. via Docker).\n2. Link your device: open `http://\u003csignal-api-host\u003e/v1/qrcodelink` in a browser.\n3. Configure the webhook URL in signal-cli-rest-api: `RECEIVE_WEBHOOK_URL=http://\u003cingestion-bot-host\u003e:\u003cport\u003e/webhook`.\n4. Create `users.json` at `--users` path or default (see Config schema below). Do not commit this file.\n5. Start the ingestion bot: `paperless-ingestion-bot signal --config /path/to/config.json`.\n\n### Gmail\n\n1. Enable IMAP in Gmail settings.\n2. Create an [App Password](https://myaccount.google.com/apppasswords) (requires 2-Step Verification).\n3. Add the account via Signal: send `gmail add user@example.com \u003capp_password\u003e` to the linked Signal number.\n4. Run the email pipeline (manually or via timer): `paperless-ingestion-bot email --config /path/to/config.json`.\n\n### Generic IMAP\n\nGeneric IMAP is supported but adding accounts via Signal isn't implemented yet. Add entries manually to `email-accounts.json` (array of account objects). Each needs `email`, `enabled`, `removed`, `exclude_labels`, `added_by`, and `details` with `type: \"generic_imap\"`, `host`, `port`, `secure`, `mailbox`. Gmail passwords go in the system keychain via `gmail add`; for generic IMAP you add credentials to the system keychain yourself.\n\n## Config\n\nJSON config (often Nix-generated via `builtins.toJSON`). Standalone: no parent-repo structure, just clone and run.\n\n**Resolution:** `--config` flag or `PAPERLESS_INGESTION_CONFIG` env or default `/etc/paperless-ingestion-bot/config.json`. 12-factor: individual values and paths override via env (e.g. `PAPERLESS_INGESTION_SIGNAL_API_URL`).\n\n**Env overrides (12-factor):** Individual env vars override file values when set: `PAPERLESS_INGESTION_PAPERLESS_URL`, `PAPERLESS_INGESTION_PAPERLESS_TOKEN`, `PAPERLESS_INGESTION_SIGNAL_API_URL`, `PAPERLESS_INGESTION_WEBHOOK_HOST`, `PAPERLESS_INGESTION_WEBHOOK_PORT`, `PAPERLESS_INGESTION_LOG_LEVEL`, etc. See schema for full list.\n\n### Schema\n\n```json\n{\n\t\"paperless_url\": \"http://127.0.0.1:8000\",\n\t\"paperless_token\": \"your-paperless-api-token\",\n\t\"signal_api_url\": \"http://127.0.0.1:8080\",\n\t\"log_level\": \"INFO\",\n\t\"webhook_host\": \"127.0.0.1\",\n\t\"webhook_port\": 8089,\n\t\"ollama_url\": \"http://127.0.0.1:11434\",\n\t\"ollama_vision_model\": \"moondream\",\n\t\"ollama_text_model\": \"llama3.2\",\n\t\"mark_processed_label\": \"paperless\",\n\t\"page_size\": 50\n}\n```\n\n- `paperless_url`: Paperless-ngx base URL (e.g. `http://127.0.0.1:8000`)\n- `paperless_token`: Paperless API token (Settings → Users → Create token)\n- **Paperless API version contract:** The bot targets a specific Paperless-ngx REST API version via the `Accept: application/json; version=N` header. See `PAPERLESS_NGX_ACCEPT_VERSION` in `src/live/paperless-client.ts`. Your paperless-ngx `ALLOWED_VERSIONS` (Settings → General) must include this version.\n- `signal_api_url`: signal-cli-rest-api base URL (e.g. `http://127.0.0.1:8080`)\n- Config path: `--config` or `PAPERLESS_INGESTION_CONFIG` (default: `/etc/paperless-ingestion-bot/config.json`).\n- Users path: `--users` or `PAPERLESS_INGESTION_USERS_PATH` (default: `/var/lib/paperless-ingestion-bot/users.json`). Create manually, don't commit.\n- Email accounts path: `--email-accounts` or `PAPERLESS_INGESTION_EMAIL_ACCOUNTS_PATH` (default: `/var/lib/paperless-ingestion-bot/email-accounts.json`). Gmail/IMAP account metadata; passwords in system keychain.\n- `webhook_host`, `webhook_port`: Signal webhook server bind address\n- `ollama_url`, `ollama_vision_model`, `ollama_text_model`: Ollama endpoint and models for AI eligibility (optional)\n- `log_level`: `DEBUG` | `INFO` | `WARN` | `ERROR`\n- `mark_processed_label`: Gmail label for processed messages; empty string disables labeling\n- `page_size`: Email fetch batch size\n\n**Env overrides:** Each key can be overridden by an env var: `PAPERLESS_INGESTION_` + UPPER_SNAKE_CASE of the key (e.g. `paperless_url` → `PAPERLESS_INGESTION_PAPERLESS_URL`).\n\nSee [config.example.json](config.example.json) for a full example. A JSON Schema is emitted at build time: `dist/config.schema.json`.\n\n**users.json** format (array of user objects):\n\n```json\n[\n\t{\n\t\t\"slug\": \"krzysiek\",\n\t\t\"signal_number\": \"+48123456789\",\n\t\t\"display_name\": \"Krzysiek\"\n\t}\n]\n```\n\n- `slug`: Unique identifier; Paperless tag is `signal-{slug}` (e.g. `signal-krzysiek`)\n- `signal_number`: User's Signal phone number\n- `display_name`: Human-readable name\n\n## Security\n\n**Security-first design.** This project takes supply chain and operational security seriously:\n\n- **Credentials** — Stored only in the OS keychain (@napi-rs/keyring). No file fallback; fails clearly when keyring unavailable. Gmail app passwords and Signal API access never touch disk in plaintext.\n- **Webhook** — Token-bucket rate limiting (120/min); excess returns 429. Same-host deployment recommended: bind to `127.0.0.1` so only local processes (signal-cli-rest-api) can reach it. See [ADR 0002](docs/adr/0002-signal-webhook-security.md).\n- **PII in errors** — Paths, emails, phones, URLs are redacted in logs via Effect `Redacted`; raw values never appear in structured logs.\n- **Supply chain** — `bun audit --audit-level=high` in every check. CycloneDX SBOM generated in CI. Dependabot, CodeQL, OpenSSF Scorecard with least-privilege workflow permissions. Release images signed with Sigstore/cosign keyless signing.\n- **File permissions** — email-accounts metadata written with mode `0600`. Run as a dedicated user.\n\n**Trust model:** All family members in the config share the same Gmail account registry. Anyone can run `gmail status` to see all configured accounts and use pause/resume/remove on any of them. The design assumes a trusted group.\n\n## Troubleshooting\n\n| Issue                                                              | Solution                                                                                                                                                                                     |\n| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `System keychain unavailable` or `File system error: getPassword at keyring` | Ensure libsecret/Secret Service is available (e.g. gnome-keyring, kwallet). On headless Linux, run a secret service or use a session with keychain support.                                  |\n| `Config file not found`                                            | Pass `--config /path/to/config.json` or set `PAPERLESS_INGESTION_CONFIG`.                                                                                |\n| `paperless_url not reachable`                                      | Ensure Paperless-ngx is running. Use `--skip-reachability-check` to bypass (e.g. Paperless starts after bot).                                                                                 |\n| `signal_api_url not reachable`                                     | Ensure Signal REST API is running. Use `--skip-reachability-check` to bypass (e.g. API starts after bot).                                                                                     |\n| `No users configured`                                              | Create `users.json` at `--users` path or `PAPERLESS_INGESTION_USERS_PATH`: `[{\"slug\":\"krzysiek\",\"signal_number\":\"+48...\",\"display_name\":\"Krzysiek\"},...]`. |\n| `No account found` for gmail commands                              | Run `gmail status` to list accounts. Add with `gmail add email@example.com \u003capp_password\u003e`.                                                                                                  |\n| IMAP connection fails                                              | Enable IMAP in Gmail; use App Password, not main password.                                                                                                                                   |\n| Ollama assessment timeout                                          | Increase model load or reduce prompt; timeout is 60s.                                                                                                                                        |\n\n## Verification\n\n```bash\nbun run check\n```\n\nRuns tests, lint, and typecheck.\n\n**CI:** GitHub Actions runs `bun run check` on push and PR. Commits must follow [Conventional Commits](https://www.conventionalcommits.org/) (enforced by commitlint). A CycloneDX SBOM is generated and uploaded as a workflow artifact.\n\n## Documentation\n\nTypeScript implementation using [Effect](https://effect.website/) and functional programming conventions. Bleeding edge: Effect v4 beta, [TypeScript Native](https://devblogs.microsoft.com/typescript/announcing-typescript-native-previews/) (`tsgo`) for build and typecheck (~10× faster than `tsc`).\n\n### Bun support policy\n\nWe target **Bun 1.3+** and Effect v4 beta. This is a deliberate choice for modern features. We intend to support the Bun version that Effect v4 officially supports. Check [Effect compatibility](https://effect.website/) and our `packageManager` field in `package.json` for the minimum supported version.\n\n- [deploy/](deploy/): Deployment recipes — [Compose](deploy/compose/README.md) (Docker) and [systemd](deploy/systemd/README.md) (service units)\n- [ARCHITECTURE.md](docs/ARCHITECTURE.md): Project structure and design\n- [RELATED_PROJECTS.md](docs/RELATED_PROJECTS.md): Potentially useful complementary projects\n- [CONTRIBUTING.md](CONTRIBUTING.md): How to contribute\n- [knirski/auto-pr](https://github.com/knirski/auto-pr): Auto-PR workflow (push to `ai/*` branches)\n- [docs/SCHEDULED_WORKFLOWS.md](docs/SCHEDULED_WORKFLOWS.md): Enable scheduled workflows (cron)\n- [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md): Community standards\n- [SECURITY.md](SECURITY.md): Vulnerability reporting\n- [SUPPORT.md](SUPPORT.md): Getting help\n- [test/integration/README.md](test/integration/README.md): Integration test guide\n- [docs/plans/COMPLETED_SUMMARY.md](docs/plans/COMPLETED_SUMMARY.md): Summary of completed plans and designs\n- [docs/adr/](docs/adr/): Architecture Decision Records\n\n**CII Best Practices:** See [docs/CII.md](docs/CII.md) for progress. Complete the [self-assessment](https://www.bestpractices.dev/en/projects/new?project_url=https%3A%2F%2Fgithub.com%2Fknirski%2Fpaperless-ingestion-bot) to earn the badge.\n\nThis project was developed with assistance from AI coding tools.\n\n## License\n\nApache 2.0. See [LICENSE](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknirski%2Fpaperless-ingestion-bot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fknirski%2Fpaperless-ingestion-bot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fknirski%2Fpaperless-ingestion-bot/lists"}