The .env.example file documented the IMAP, NocoDB, SeaweedFS, Papra (bare connection vars), Ollama base URL, and Gitea env blocks, but was missing 7 env vars that the workflows reference: 1. OLLAMA_API_KEY="" — empty by default (Ollama local is unauthenticated; the entry is for FRP-tunneled or remote Ollama where the operator has set a key) 2. INBOXOS_LLM_BACKEND=ollama — default backend; the v1.0 production path is Ollama. The "opencode" backend is wired in pipelines/common.py::opencode_run_command but documented as offline-tests-only (common.py:455-461). 3. PAPRA_DRY_RUN=true — the v1.0 + v1.1 + v1.2 default. Set to "false" to enable live Papra writes (the Phase 6 / future-v1.x gate per docs/papra-write-policy.md). 4. PAPRA_BODY_WRITE_MODULES=crm,finance,legal — modules whose bodies (not just attachments) get written to Papra on the post-classify pass. 5. PAPRA_METADATA_ONLY_MODULES=intel,promotions — modules whose Papra write is metadata-only (no body upload). 6. MBOX_BUCKET= — historical pipeline's S3 bucket for the .mbox source; defaults to $SEAWEEDFS_S3_BUCKET if unset. 7. OPENCODE_NPM_VERSION=latest — historical pipeline's opencode CLI version. This commit adds a new "Workflow tunables" section at the end of .env.example containing all 7 commented-out entries with workflow-baked defaults and 1-line explanations. The INBOXOS_LLM_MODEL / INBOXOS_LLM_MODEL_EXTRACT block (lines 96-99) is unchanged (REQ-INB-15 / v1.1 P1-5 closure). Verified: no literal token values are introduced (the v1.0 secret-scan convention is honored: pre-existing NOCODB_API_TOKEN=change-me-* / GITEA_PASS=change-me-* / IMAP_PASSWORD=change-me-* are placeholder values, not real secrets). No cto-inboxos references are introduced (REQ-INB-28 / REQ-INB-37 / v1.1 P0 sweep closure preserved). ---ci--- project: inboxos phase: 1 milestone: v1.3 status: execute tasks_completed: [T3.1] candidates_closed: [REQ-INB-33] files_touched: - .env.example (1 file, 45 lines added at the end, new "Workflow tunables" section) ---/ci---
InboxOS
Self-hosted personal ERP / CRM pipeline. A single Gmail inbox, polled over IMAP, archived to SeaweedFS, classified and extracted by a local LLM (Ollama), and written to NocoDB as structured records. Curated attachments are mirrored to Papra. No third-party SaaS touches PII.
Where to start
- Operations runbook —
docs/operations-runbook.md. System overview, service inventory, workflow operations, observability, failure modes, security, cost, glossary. The first thing to read on day 1 of incident response. - Extraction contract —
docs/extraction-contract.md. How a raw email becomes a NocoDB record. Source of truth for theinboxos:classify+inboxos:extract-<module>prompt family. - NocoDB schema —
docs/schemas.md. The four module tables (finance,crm,intel,promotions) and the Message-ID idempotency contract. - Gitea Actions setup —
docs/gitea-actions.md. Runner provisioning (including the second runner for live + historical parallelism), repo secrets, the Basic Auth model. - Pipelines —
pipelines/README.md. Flow diagrams, idempotency model, rollback procedure for the live and historical pipelines. - Papra write policy —
docs/papra-write-policy.md. When the pipeline writes to Papra (attachments + selected bodies), and why the first ship is dry-run.
Quick layout
inboxos/
├── README.md # this file
├── .env.example # placeholder env (no real secrets)
├── docker/
│ └── docker-compose.yml # self-hosted stack: gitea, runners, nocodb, seaweedfs, papra, ollama
├── .gitea/workflows/
│ ├── live-pipeline.yml # cron */5 * * * *, polls IMAP
│ ├── historical-pipeline.yml # workflow_dispatch, streams .mbox
│ └── ci-pr.yml # fast-tests + 100MB e2e + live Ollama smoke
├── pipelines/ # shared Python: common.py, live_pipeline.py, historical_pipeline.py, mbox_stream.py
├── .opencode/commands/ # version-controlled extraction prompts (prompt-as-code)
├── config/ # n8n config (legacy, no longer in use)
└── docs/ # this runbook + schemas + contracts + reports
Status
Phase 5 — Full Deployment. Parent goal: INB-1. Current phase issue: INB-7. The runbook in docs/operations-runbook.md is the canonical reference for ongoing operation.
Historical archive (Google Takeout .mbox)
The full Google Takeout .mbox export lives in SeaweedFS at the canonical S3 URI:
s3://inboxos/eml/historical/all-mail.mbox
That is the value to pass as the mbox_object_key workflow_dispatch input to historical-pipeline.yml. To verify it exists, run from any host with mc configured against the inboxos bucket:
mc ls --recursive inboxos/inboxos/ | grep '\.mbox$'
The historical pipeline streams this object message-by-message, UPSERTs each into NocoDB on Message-ID, and writes the raw .eml backup under s3://inboxos/eml/historical/<yyyy>/<mm>/<dd>/<Message-ID>.eml. See docs/operations-runbook.md §3.2 for the dispatch recipe and §4.4 for the full bucket layout.