# InboxOS — Environment Configuration # Copy to .env and fill in secrets # cp .env.example .env # ── NocoDB ────────────────────────────────────────────── NOCODB_JWT_SECRET=change-me-to-a-random-string NOCODB_BASE_URL=https://noco.cloudinit.dev NOCODB_API_TOKEN=change-me-to-a-nocodb-api-token NOCODB_PROJECT_ID=change-me-to-nocodb-project-id # NOCODB_MODULE_TABLES is a JSON object that maps each module name # returned by `inboxos:classify` to the NocoDB table id that should # receive its extraction record. Phase 2 (INB-4) is responsible for # populating this; an empty map is tolerated (the pipeline will # log `module_table_missing` and dead-letter the message). # # Example: # NOCODB_MODULE_TABLES='{"finance":"tbl_finance_xxx","crm":"tbl_crm_yyy","intel":"tbl_intel_zzz","promotions":"tbl_promo_www"}' NOCODB_MODULE_TABLES='{"finance":"tbl_finance_placeholder","crm":"tbl_crm_placeholder","intel":"tbl_intel_placeholder","promotions":"tbl_promo_placeholder"}' # ── SeaweedFS (S3-compatible object store) ────────────── # Used for raw .eml archival and pipeline state (checkpoints). SEAWEEDFS_S3_ENDPOINT=https://storage.cloudinit.dev SEAWEEDFS_S3_ACCESS_KEY=change-me-to-a-seaweedfs-access-key SEAWEEDFS_S3_SECRET_KEY=change-me-to-a-seaweedfs-secret-key SEAWEEDFS_S3_BUCKET=inboxos # ── Papra ──────────────────────────────────────────────── PAPRA_AUTH_SECRET=change-me-to-a-random-string PAPRA_WEBHOOK_SECRET=change-me-to-another-random-string PAPRA_BASE_URL=https://papra.cloudinit.dev PAPRA_API_TOKEN=change-me-to-a-papra-api-token PAPRA_ORGANIZATION_ID=change-me-to-papra-org-id # ── Ollama ────────────────────────────────────────────── # OLLAMA_HOST=0.0.0.0 # Base URL the OpenCode CLI uses to reach Ollama. Same value is # injected into Gitea Action secrets as OLLAMA_BASE_URL. OLLAMA_BASE_URL=https://ollama-frp.cloudinit.dev/v1 # ── Gitea ─────────────────────────────────────────────── # GITEA__server__DOMAIN=gitea.cloudinit.dev # Base URL for the Gitea REST API. Inside the gitea-runner container # this is the compose service name; from a host or external agent # use the public hostname (https://git.cloudinit.dev). GITEA_INSTANCE_URL=http://gitea:3000 # HTTP Basic Auth credentials for any code that calls the Gitea # REST API via pipelines.common.gitea_request. We use Basic Auth # rather than a personal access token because Gitea does not # return the plaintext of a freshly created access token through # the API — Basic Auth is the only fully headless path. See # docs/gitea-actions.md §5 for the rationale. GITEA_USER=cto GITEA_PASS=change-me-to-the-gitea-service-account-password # Registration token from Gitea → Site Administration → Actions → Runners # → "Create new runner". This is a one-time secret the runner uses to # register itself; the runner then receives a long-lived token that # lives in the gitea-runner-data volume. GITEA_RUNNER_REGISTRATION_TOKEN=change-me-to-a-gitea-runner-registration-token # Registration token for the second runner (gitea-runner-2). Generate a # fresh one from the same Gitea UI — each runner needs its own token. # Stored long-lived in the gitea-runner-2-data volume. See INB-33 for # the rationale (live + historical parallelism on the same host). GITEA_RUNNER_REGISTRATION_TOKEN_2=change-me-to-a-second-gitea-runner-registration-token # ── IMAP (Live Pipeline) ──────────────────────────────── # Use a Gmail App Password, not the account password. The Gmail # account should have IMAP enabled in its forwarding/IMAP settings. IMAP_HOST=imap.gmail.com IMAP_PORT=993 IMAP_USER=inboxos.dummy@gmail.com IMAP_PASSWORD=change-me-to-a-gmail-app-password IMAP_USE_SSL=true IMAP_MAILBOX=INBOX # ── OpenCode (executed by the Gitea Actions runner) ───── # OPENCODE_BIN is the path to the `opencode` binary on the runner. # OPENCODE_PROMPT_DIR is where version-controlled extraction prompts # live (e.g. inboxos:extract-finance.md under .opencode/commands/). OPENCODE_BIN=opencode OPENCODE_PROMPT_DIR=./.opencode/commands # Optional overrides for the OpenCode command names. Defaults match # the prompts checked into the repo, so these are only needed if you # rename the prompt files. # INBOXOS_CLASSIFY_COMMAND=inboxos:classify # INBOXOS_EXTRACT_COMMAND_PREFIX=inboxos:extract- # ── Pipeline tunables ────────────────────────────────── # LOG_LEVEL=INFO # INBOXOS_BODY_CHARS=2000 # cap on body text sent to the LLM (2000 ~= 500 tokens) # INBOXOS_LLM_MAX_TOKENS=4096 # LLM completion budget; 1024 was too tight for # # the 11-field extract schema on rich emails # # (see INB-21). # INBOXOS_LLM_MODEL and INBOXOS_LLM_MODEL_EXTRACT are pinned to gemma4:e4b. The legacy two-model split (cheap classify + larger extract) was retired in v1.0; REQ-INB-11 forbids a model-escalation chain. # INBOXOS_LLM_MODEL=gemma4:e4b # canonical pin per REQ-INB-11 (do not change in production) # INBOXOS_LLM_MODEL_EXTRACT=gemma4:e4b # same as INBOXOS_LLM_MODEL; the legacy two-model split was retired in v1.0 # INBOXOS_LLM_TIMEOUT=120 # seconds per LLM call # ── Workflow tunables (REQ-INB-33) ────────────────────── # These env vars are referenced by the Gitea Actions workflows # (.gitea/workflows/live-pipeline.yml, historical-pipeline.yml, # ci-pr.yml) but are not surfaced in the example file. Defaults # below match the workflow-baked defaults; uncomment + edit to # override locally. # OLLAMA_API_KEY="" # empty by default; Ollama local is # unauthenticated. Set only if your # Ollama is FRP-tunneled and auth-gated. # INBOXOS_LLM_BACKEND=ollama # default; 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); # do not flip the default without # reading the docstring. # PAPRA_DRY_RUN=true # 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). # PAPRA_BODY_WRITE_MODULES=crm,finance,legal # modules whose bodies # (not just attachments) # get written to Papra # on the post-classify # pass. # PAPRA_METADATA_ONLY_MODULES=intel,promotions # modules whose Papra # write is metadata-only # (no body upload). # MBOX_BUCKET= # historical pipeline's S3 bucket for # the .mbox source. Defaults to # $SEAWEEDFS_S3_BUCKET if unset. # OPENCODE_NPM_VERSION=latest # historical pipeline's opencode CLI # version (the opencode backend is # offline-tests-only in production # but the env var is still referenced # by pipelines/common.py). # ── end Workflow tunables ────────────────────────────── # ── v1.4 notes ────────────────────────────────────────── # The workflow YAML auto-masks the above secret values in CI # logs via `::add-mask::` (REQ-INB-56). Do not echo them in # custom `run:` steps; the mask only protects values Gitea # Actions sees, not values that are echoed in plain text.