Operations¶
This page covers the operator-facing surfaces as the code implements them: the admin HTTP API, readiness, the local run ledger, logging, OpenTelemetry export and webhook notifications. For symptom-driven troubleshooting — failed runs, log shapes, squatter cleanup, the weekly review — use the Runbook, which is the Influx repo's docs/operations/runbook.md imported at build time.
Admin HTTP API¶
influx serve binds FastAPI to INFLUX_ADMIN_BIND_HOST:INFLUX_ADMIN_PORT (default 127.0.0.1:8080). There is no authentication: a non-loopback bind is refused unless [security] allow_remote_admin = true.
| Method and path | Returns |
|---|---|
GET /live |
200 {"live": true} while the process is up |
GET /ready |
200 {"ready": true} when cached probes pass, else 503 {"ready": false} |
GET /status |
Always 200: status, ready, version, dependencies.lithos.status, dependencies.llm_credentials.status, per-profile {scheduled, currently_running, next_run_at, last_run_at, last_run_status}, and an inbox block when the inbox is enabled |
GET /runs/recent?limit=20&profile=<name> |
{"active": [...], "runs": [...]} from the run ledger |
POST /runs |
Body {"profile": "<name>"} or {"all_profiles": true}. 202 with request_id, kind, scope, submitted_at |
POST /backfills |
Body {"profile"|"all_profiles", "days"|("from","to"), "confirm"?}. Same 202 shape |
Health endpoints read cached state from the probe loop and the last inbox tick; they never probe Lithos or list tasks per request.
Error responses are compact JSON aligned with the Lithos envelope:
{"status": "invalid_request", "error": "unknown_profile", "message": "Unknown profile: 'x'", "reason": "unknown_profile"}
| Status | error |
When |
|---|---|---|
422 |
unknown_profile, invalid_request |
Profile not in config; body fails validation (detail lists the errors) |
409 |
profile_busy |
The profile's coordinator lock is held; includes active_run from the ledger when known |
400 |
confirm_required |
Backfill estimate above 1000 without confirm; includes estimated_items |
all_profiles acquires every profile lock all-or-nothing: if any profile is busy, locks already taken are released and the request is 409. There is no cancel endpoint; restarting the service marks in-flight ledger entries abandoned.
Readiness and probes¶
A background ProbeLoop runs every 30 seconds and caches:
- Lithos — opens an SSE connection to
lithos.urland expects HTTP 200 (noagent_registercall). After 3 consecutive degraded cycles the circuit opens and every Run is skipped with ledgerstatus="skipped", error="lithos_unhealthy"until the next OK probe. - LLM credentials — presence of each provider's
api_key_envin the environment (no remote call). - LCMA tools — the Lithos tool list includes the tools Influx needs; otherwise runs skip with
lcma_tools_unavailable.
/ready is 503 when any probe fails, the cached state is stale (older than 3 intervals), a repair-sweep terminal write failure is latched, or the LCMA latch is set. The repair latch clears on the next successful sweep.
Run ledger¶
Operational history lives under storage.state_dir (default /state), not in Lithos:
<state_dir>/
├── active-runs.json # in-flight runs keyed by run_id
├── runs.jsonl # append-only terminal history
└── unresolved-slug-collisions.jsonl # writes that survived the recovery chain
Each entry carries run_id, profile, kind (scheduled | manual | backfill | inbox), status (running | completed | failed | skipped | abandoned), run_range, started_at, completed_at, duration_seconds, sources_checked, ingested, cache_hits, error, degraded and degraded_reasons. Manual runs reuse the HTTP request_id as run_id; scheduled runs get a UUID. On start-up, leftovers in active-runs.json are marked abandoned.
degraded_reasons values you will see:
| Reason | Meaning |
|---|---|
source_acquisition |
At least one source fetch failed and was swallowed |
archive_acquisition |
At least one archive download failed (note written with influx:archive-missing) |
source_cooldown_skip |
arXiv or a feed was skipped because it is in cooldown |
dedup_cache_lookup |
A lithos_cache_lookup failed for a candidate (skipped this run) |
ingestion_stall |
This and the previous scheduled run ingested 0 despite candidates |
fetch_stall |
This and the previous scheduled run fetched 0 from every source |
filter_stall |
Sources fetched normally but the filter rejected everything, twice in a row |
filter_error |
The filter scorer raised at least once this run |
The three stall reasons are mutually exclusive on a run; backfills and inbox runs are excluded from stall detection. scripts/influx-diagnose.py recent|failures|run read these files directly.
Logging¶
Logs go to stderr as single-line JSON with the same core fields Lithos uses — timestamp, level, logger, message — plus any structured extras (run_id, profile, source_url, status, detail, note_id, sweep_stage, …). httpx/httpcore are quietened to WARNING.
INFLUX_LOG_LEVEL=DEBUG # Python level name; default INFO
INFLUX_LOG_FORMAT=text # plain text for local work; default json
With INFLUX_OTEL_ENABLED=true the same records are also forwarded to an OTEL LoggingHandler, so every extra field becomes a log attribute in your collector; the stderr stream is unchanged.
Telemetry¶
Influx exports traces, metrics and logs by OTLP push to a collector you run; nothing is scraped from the process. The feature is opt-in and controlled by environment variables (the [telemetry] TOML block is validated but not read by the exporter wiring at the current main):
| Variable | Effect |
|---|---|
INFLUX_OTEL_ENABLED=true |
Turn on all three signals (requires the otel extra, which the Docker image installs) |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP/HTTP collector base URL; per-signal OTEL_EXPORTER_OTLP_{TRACES,METRICS,LOGS}_ENDPOINT also honoured |
INFLUX_OTEL_CONSOLE_FALLBACK=true |
With no endpoint set, print spans and metrics to stdout |
OTEL_SERVICE_NAME |
service.name resource attribute (default influx) |
INFLUX_ENVIRONMENT |
deployment.environment resource attribute (dev, staging, prod) |
When OTEL is disabled every telemetry call is a boolean check and returns; no SDK objects are built. Spans cover the run, source fetches, archive downloads, enrichment calls, Lithos writes and retrieves.
Metric instruments (label values are bounded; per-item ids are never labels):
| Instrument | Type | Labels |
|---|---|---|
influx_run_starts_total |
counter | profile, run_type |
influx_run_completions_total |
counter | profile, run_type, outcome (success | failure | degraded) |
influx_run_duration_seconds |
histogram | profile, run_type |
influx_active_runs |
up-down counter | profile |
influx_runs_skipped_total |
counter | profile, reason |
influx_source_candidates_fetched_total |
counter | profile, source |
influx_articles_filtered_total |
counter | profile, decision (pass | drop) |
influx_articles_inspected_total |
counter | profile, source |
influx_cache_hits_total |
counter | profile, source |
influx_lithos_writes_total |
counter | profile, source, status |
influx_repair_candidates_total |
counter | profile, kind (archive | text_extraction | tier2 | tier3) |
influx_llm_validation_failures_total |
counter | profile, tier (1 | 3) |
influx_archive_missing_total |
counter | profile, source |
influx_source_acquisition_errors_total |
counter | profile, source, kind |
influx_ingestion_stall_runs_total |
counter | profile, reason |
influx_slug_collision_dedup_recovery_total, …_reclaimed_total |
counter | none |
influx_slug_collision_unresolved_total |
counter | profile, source |
influx_inbox_tick_started_total, …_tasks_listed_total, …_tasks_claimed_total |
counter | none |
influx_inbox_items_processed_total |
counter | outcome (ingested, filtered_out, cache_hit, profile_busy_skipped, error, invalid_submission, invalid_source_tag, pdf_rejected, filter_unavailable_deferred, filter_unavailable) |
influx_inbox_task_call_failures_total |
counter | phase (list | claim | update | release | complete) |
The runbook's §7 gives PromQL for the common questions. The same collector layout used for Lithos (Observability) works for Influx; filter by service.name.
Notifications¶
After a completed run (never after a backfill; inbox runs only for sinks that list inbox in notify_on) Influx POSTs to each enabled [[notifications.webhooks]] sink whose notify_on includes the run kind. Delivery goes through the guarded HTTP client with notifications.timeout_seconds, follows no redirects, and counts only 2xx as success — a 302 /login is logged as a failure. Failures never fail the run.
| Type | Event modes | Payload |
|---|---|---|
generic_digest |
digest |
Zero-ingest run: quiet digest with message and stats. Otherwise highlights (items with score >= notify_immediate), all_ingested, stats.high_relevance |
agent_zero_message_async |
digest, article |
{"text", "context"} to Agent Zero's /api/message_async (session-authenticated when Agent Zero login is on) |
agent_zero_notification_create |
article |
One toast payload per matching article |
agent_zero_rfc_message |
digest, article |
{"rfc_input", "hash"} to /api/rfc, naming rfc_module.rfc_function with text and context; the shared secret comes from rfc_password_env. The helper to deploy inside Agent Zero is integrations/agent_zero/usr/influx_rfc.py (see the repo's docs/AGENT_ZERO_RFC.md) |
openclaw_agent |
digest, article |
{"message", "name", "deliver"} plus wakeMode and channel when set, to OpenClaw's /hooks/agent |
Bearer tokens come from auth_token_env; a sink whose token variable is unset is skipped with a warning. The legacy notifications.webhook_url sends a single generic_digest when no typed sinks are configured.
Security boundaries¶
- Outbound HTTP — every fetch and webhook POST passes the guarded client:
http/httpsonly, DNS resolution with private-IP blocking unlesssecurity.allow_private_ips, streaming size cap (storage.max_download_bytes), timeouts, content-type checks, redirect re-validation. - Admin bind — loopback unless
security.allow_remote_admin. - Archive paths — item ids with traversal or absolute components are rejected; resolved paths must stay under
archive_dir; inboxlocal_pathmust resolve underpdf_root. - Secrets — only ever named by environment variable (
api_key_env,auth_token_env,rfc_password_env), never written ininflux.toml.