cloudinit-bot 94f4233619 docs(verify): v1.3 phase 1 verification PASS (4 layers, 9 must-haves, 2 backlog closures, high confidence)
The v1.3 EXECUTE stage landed 8 PRs / 8 commits closing
9 ACCEPT candidates (REQ-INB-28, 29, 30, 31, 32, 33, 34,
36, 37). The 4-layer verification produces a clean PASS.

Structural layer: 8/8 must-haves PASS
- no cto-inboxos references in source-tree (T1.1)
- ci-pr.yml wires all 6 v1.1 offline tests (T1.2)
- extraction-contract.md line refs are post-v1.1 (T2.4)
- papra-write-policy.md test count is 15 (T2.3)
- runbook footer says v1.3 (T2.1)
- runbook §6 ordering is 6.1 → 6.2 → 6.3 → 6.4 (T2.5)
- runbook §4.5 forward-pointer says v1.3 (T2.2)
- .env.example has 7 new workflow tunables (T3.1)

Behavioral layer: 30/30 runnable offline tests PASS
- 3 pre-existing fast-tests: 10 + 9 + 3 = 22/22 (live_pipeline
  11 deferred-runtime per v1.0/v1.1 precedent)
- 6 new v1.1 hard-fail tests: 15 + 3 + 2 + 7 + 3 = 30/30
- 1 deferred-runtime test (test_papra_link_parity): expected
  fail per v1.0/v1.1 precedent; ci-pr.yml step uses
  continue-on-error: true

Security layer: 4/4 STRIDE categories PASS
- no cto-inboxos in source-tree (5 hits in .ciagent/ are
  audit-trail records describing the migration, not
  source-tree references)
- .env is git-ignored
- 8/8 execute commits have well-formed ---ci--- blocks
- no literal token values in .env.example
- ci-pr.yml YAML parses cleanly

Quality layer: 6/6 dimensions PASS
- 9/9 REQ-INB-* coverage (the 3 DEFER candidates are filed
  in v1.3-candidates.md §9 for v1.4+)
- 6/6 active personas represented (devops-engineer primary
  on T1.1/T1.2/T3.1; security-engineer review on T1.1/T3.1;
  lead-developer primary on T2.1-T2.5; ml-engineer review on
  T2.4; data-engineer + frontend-engineer are territory-not-
  invoked / deactivated respectively)
- 8/8 commit discipline (all <type>(<scope>):, max 2 files)
- 9 unique file touches (no .py changes, no new tests, no
  new NocoDB tables, no new shell scripts, no new deps)
- docs/requirements.md + docs/roadmap.md byte-identical to
  v1.2 close (CPO-owned, not modified)
- 0 cross-project (paperclip unmodified)

v1.1 backlog closure: 2/4
- P1-1 closed by REQ-INB-36 (runbook §6 renumber)
- P1-2 closed by REQ-INB-37 (setup_gitea_secrets.sh cto-inboxos)
- P2-1 + P2-2 deferred to v1.4+ (filed in v1.3-candidates.md §9)

Verdict: PASS, confidence high. 0 P0/P1/P2 issues, 0 blockers.
The v1.3 inboxos milestone is ready for the REVIEW and AUDIT
stages.

---ci---
project: inboxos
phase: 1
milestone: v1.3
status: verify
verification:
  verdict: PASS
  confidence: high
  layers:
    structural: PASS (8/8)
    behavioral: PASS (30/30 runnable)
    security: PASS (4/4 STRIDE)
    quality: PASS (6/6)
  must_haves: 9/9
  must_haves_table:
    - {n: 1, status: PASS, location: docs/gitea-actions.md:95}
    - {n: 2, status: PASS, location: docs/operations-runbook.md:823}
    - {n: 3, status: PASS, location: docs/operations-runbook.md:285-289}
    - {n: 4, status: PASS, location: docs/papra-write-policy.md:192,202}
    - {n: 5, status: PASS, location: docs/extraction-contract.md:40,98,102,161}
    - {n: 6, status: PASS, location: .env.example:100-144 (new Workflow tunables section)}
    - {n: 7, status: PASS, location: .gitea/workflows/ci-pr.yml (6 new test steps)}
    - {n: 8, status: PASS, location: docs/operations-runbook.md:479,511,561,568 (§6 monotonic)}
    - {n: 9, status: PASS, location: scripts/setup_gitea_secrets.sh:4,14,18,28}
  backlog_closures:
    v1_1_P1_1: REQ-INB-36 (closed)
    v1_1_P1_2: REQ-INB-37 (closed)
    v1_1_P2_1: REQ-INB-38 (deferred to v1.4+)
    v1_1_P2_2: REQ-INB-39 (deferred to v1.4+)
  tests:
    pre_existing_pass: 22/22
    v1_1_hard_fail_pass: 30/30
    deferred_runtime: 12
    deferred_reason: "imapclient missing in test host (v1.0 / v1.1 accepted precedent)"
  p0_issues: 0
  p1_issues: 0
  p2_issues: 0
  blockers: []
---/ci---
2026-06-08 23:33:08 +00:00

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 the inboxos: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.

S
Description
InboxOS — self-hosted personal ERP/CRM pipeline. Prompts-as-code, Gitea Actions, NocoDB, Ollama, SeaweedFS, Papra.
Readme 1.8 MiB
Languages
Python 90.3%
Shell 9.7%