Skip to content
Getting Started

Getting Started

RunLore reacts to incidents, investigates them with an LLM (grounded in your knowledge catalog), delivers findings to chat, and — optionally — curates what it learns back to a Git repo as pull requests. This page has three stops, each independently worth doing on its own:

  1. Try it — a real root cause, no cluster, no keys, ~60 seconds.
  2. Investigate your own stack — one CLI command against your cluster, no config file.
  3. The full looplore serve in-cluster: continuous reaction, chat delivery, and knowledge curation.

Try it — no cluster, no keys

Before wiring anything, watch RunLore reach a real root cause over recorded evidence — no Kubernetes, no LLM key, no network. You only need Go and git:

git clone https://github.com/Smana/runlore && cd runlore
hack/demo.sh

hack/demo.sh builds the binary and runs lore demo investigate --offline default — replaying a transcript recorded once against a live model through the real investigation loop: the same ReAct tool calls, verify pass, and verdict-card renderer that run in lore serve, over fake (but realistic) evidence for a Harbor chart-bump incident:

== RunLore demo: investigating "harbor-chart-bump" (recorded model turns, fake providers, no cluster) ==
   model turns recorded 2026-08-02T07:48:52Z with openai/glm-4.5-air

→ what_changed()
  flux Kustomization/apps aaa111..bbb222 --- apps/harbor/values.yaml - tag: 1.14.2 + tag: 1.15.0 …

== submit_findings ==
*Investigation* — confidence 90%
🔥 Verdict: Action required
Resource: Deployment apps/harbor-core
1. *Database migration deadlock preventing harbor-db pod from starting, causing harbor-core
   connection failures* (90%)
   → suggested: Investigate and resolve the migration deadlock in harbor-db pod, then restart
     the database to clear the lock (reversible)

That model turns recorded … with openai/glm-4.5-air line is not decoration — it’s the honesty mechanism. Nothing here is scripted for the demo: the model’s reasoning was captured once against a live model and is replayed byte-for-byte, so the card discloses exactly when and with which model it was produced, rather than letting you mistake a replay for a live run. Model behavior on the same evidence can drift release to release, so that provenance line is what tells you how current the transcript is. Re-record it yourself against your own model with --record — see the CLI reference.

Already have lore installed (see below)? The command hack/demo.sh runs is just:

lore demo investigate --offline default

run from inside a clone — the demo fixtures under examples/ are read from disk at startup, not embedded in the released binary, so this one needs the repo checked out.

Curious about the filter that decides what gets investigated in the first place, rather than the investigation itself? hack/demo-trigger-policy.sh fires mocked Alertmanager alerts through the trigger policy — no LLM involved at all.


Investigate your own stack — kubeconfig + one API key

Ready to point it at something real? lore investigate runs one on-demand investigation against your cluster and prints the findings — no runlore.yaml, no Helm install. First, get the binary:

curl -fsSL https://runlore.io/install.sh | sh
# or: go install github.com/Smana/runlore/cmd/lore@latest

Then, with a working kubeconfig and one model credential exported:

export ANTHROPIC_API_KEY=sk-ant-...          # or OPENAI_BASE_URL (+ OPENAI_API_KEY / OPENAI_MODEL)
lore investigate --alert "HarborProbeFailure" --namespace apps

With no --config, investigate synthesizes a model-only config straight from the environment — every data source stays unset, so each one simply disables the tool it would have unlocked rather than demanding a full stack:

Env varEffect
OPENAI_BASE_URL (+ optional OPENAI_API_KEY, OPENAI_MODEL)any OpenAI-compatible endpoint — keyless if the endpoint itself needs no credential (e.g. a local vLLM/Ollama)
ANTHROPIC_API_KEYnative Anthropic, checked if no OPENAI_BASE_URL is set

Four flags override whatever that resolves to, so you can point the CLI at your stack without writing a file at all:

FlagOverrides
--modelthe model name
--base-urlthe OpenAI-compatible endpoint
--metrics-urlPromQL endpoint — enables the query_metrics tool
--logs-urllogs endpoint — enables the query_logs tool

investigate never refuses to run just because a signal is missing — it runs on whatever’s configured and says so on stderr:

note: running without metrics (query_metrics), logs (query_logs), knowledge catalog (kb_search, instant recall) — pass --metrics-url/--logs-url or a --config to enable them

That notice means “under-configured,” not “broken.” The investigation still runs to completion and still reaches a verdict — just over whatever evidence is actually available. Treat it as a checklist for widening the picture (another flag, a --config pointing at a fuller runlore.yaml), not as an error to work around. Full flag/env reference: CLI.


The full loop — production install

This is where RunLore stops being a one-off lookup and starts reacting to incidents continuously, delivering to chat, and — once a knowledge-catalog repo is wired up — curating what it learns back as pull requests (the Learn loop, RunLore’s differentiator). Running in-cluster (lore serve, via Helm) is the recommended way to get there — the CLI above is for one-off local runs against the same engine.

RunLore is read-only on your cluster: it never mutates workloads. Its only writes go to the Git forge (issues/PRs on a repo you designate).

For local development / testing on k3d, see CONTRIBUTING.md instead.

The walkthrough below wires one golden path — Prometheus/VictoriaMetrics metrics, GitHub for curation, Slack for delivery, and Anthropic for the model. That is what the shipped values profiles and the Secret in Step 3 use: one API key, no endpoint of your own to run. Pointing it at any OpenAI-compatible endpoint instead is a two-line change (provider: openai + base_url) — and that is the combination the nightly eval and the k3d e2e suite exercise, so neither path is untested. Every other data source, LLM, notifier, or forge is equally supported; each gets a one-line pointer to its own page under Integrations as it comes up below.

Prerequisites

Required

  • A Kubernetes cluster — any conformant cluster works: EKS, GKE, AKS, or local k3d / kind (follow each project’s install docs).
  • kubectl + helm (v3.12+).
  • An LLM — the shipped values profiles use native Anthropic (one API key, nothing to run); any OpenAI-compatible endpoint (in-cluster vLLM, Ollama, OpenAI, OpenRouter) is a two-line swap. Keep the model in-cluster if you don’t want telemetry to leave your boundary — see Local / keyless.

Recommended

  • Flux or Argo CD running on that cluster — select with config.gitops.engine (flux default, or argocd). This unlocks the what-changed spine and the GitOps-failure trigger (Flux Kustomization/HelmRelease, or Argo CD Application, going Ready=False) — RunLore’s sharpest signal. Not required: every data source is pluggable, and an unset one just disables the tool it would have unlocked — see GitOps failures. Without it, RunLore still reacts to Alertmanager alerts and investigates with whatever other signals you’ve wired.
  • A GitHub App for curation — Step 2 below. Without it the Learn loop (curation) is disabled: RunLore still reacts and investigates, it just can’t write what it learns back to your KB repo. Since the learning loop is RunLore’s differentiator, this is strongly recommended.

Optional

Everything else is genuinely pluggable — an unset one just disables the tool or channel it would have unlocked, nothing else changes. Full catalog: Integrations.


Step 1 — Create the knowledge-catalog repo

This Git repo is where RunLore commits what it learns — every resolved incident is curated back here as a PR (the Learn loop), and the agent reads from it to ground future investigations. It’s an OKF knowledge catalog: a Git repo of markdown files, each with YAML frontmatter. This is your portable knowledge base — runbooks, past incidents, platform constraints.

  1. Create a new (private) Git repo, e.g. your-org/runlore-kb.

  2. Add entries as markdown files — one file per entry (YAML frontmatter + a markdown body). Name each file with a short, descriptive kebab-case slug (e.g. helmrelease-upgrade-failure.md); the slug is just the entry’s identity — not indexed, not a frontmatter field (RunLore names the entries it drafts <title-slug>-<8 fingerprint chars>.md, so two entries sharing a title can’t collide). Put them at the repo root or in subfolders (playbooks/, incidents/, …); the whole tree is indexed recursively. Example:

    ---
    type: Playbook
    title: HelmRelease upgrade failure for shop-api
    description: A Helm chart bump leaves the release Ready=False.
    resource: shop-prod/shop-api
    tags: [flux, helmrelease, upgrade, shop-prod]
    ---
    # Symptom
    Ready=False after a chart bump; often a DB migration that didn't complete.
    
    # Checks
    - `flux get hr -A | grep -i false`
    - the rendered diff between the two chart versions

    resource is the affected workload as namespace/name, with no whitespace — it’s what recall’s structural filter matches on, so a scoped entry beats a general one. It is required for Incident entries and optional elsewhere, but an entry with no resource can only be recalled by an incident that itself carries no workload, so leave it out only for genuinely platform-wide notes. index.md, log.md and any readme.md are reserved (a human listing + a changelog) and skipped by the indexer — as are dot-files and hidden directories. What search actually matches is one combined corpus per entry — title + description + resource + tags + body, not the filename — so write those well. Seed it with whatever runbooks you already have; the agent gets sharper at your platform as the catalog grows.

    Writing entries by hand? The full field-by-field contract lives in okf-format.md, and lore validate-kb <catalog-dir> checks a catalog against it.

  3. Make it available in-cluster. Two options:

    Option A — Git-sync (recommended; closes the read/write loop). RunLore clones the repo and re-pulls it on an interval, re-indexing automatically. When curation merges a PR into this repo, the new knowledge flows straight back into what the agent searches — no manual step. Configure it under config.catalog.git (step 4) and set catalog.gitSync: true (which mounts a writable mirror). A private repo authenticates with the same curation GitHub App by default (step 2) — one credential for both reads and writes; set git.token_env only to use a different token.

    Option B — ConfigMap (static). Mount a snapshot; refresh it yourself when the repo changes:

    git clone https://github.com/your-org/runlore-kb /tmp/runlore-kb
    kubectl -n runlore create configmap runlore-catalog \
      --from-file=/tmp/runlore-kb/ \
      --dry-run=client -o yaml | kubectl apply -f -

Step 1b — Seed the catalog from your existing runbooks (optional)

You don’t have to start empty. If your team already keeps markdown runbooks or postmortems anywhere, import them and get recall value on day one:

# preview what would be written — nothing is touched
lore kb import ./our-runbooks --into ./kb --dry-run

# convert + validate + dedup, then write into your local KB checkout
lore kb import ./our-runbooks --into ./kb
cd kb && git add . && git commit -m "seed catalog from existing runbooks" && git push

What import does, deterministically (no model, no config needed beyond the directory paths):

  • Adds/normalizes OKF frontmatter — title from the existing frontmatter or the first heading (filename as last resort), description from the first paragraph, tags from the document’s own tags plus detected alert names (KubePodCrashLooping-style tokens in headings and alert-mentioning lines — exactly the recall signal that lets a future alert find the runbook).
  • Classifies — a document that already carries Symptom/Cause/Resolution sections and names a resource becomes an Incident; everything else is a Playbook (free-form runbooks validate relaxed, same as hand-written entries).
  • Validates every entry with the same merge gate as lore validate-kb — nothing is written that the gate would later reject.
  • Dedups against what the catalog already holds (exact and fuzzy title, same rule the curator uses for duplicate PRs) and skips it with a printed reason.

Nothing is committed for you: you review the diff and push, the same human-in-the-loop bar as every RunLore KB PR. With --model, the LLM already configured in your runlore.yaml refines titles/descriptions/tags (purely optional — a model failure falls back to the deterministic result). Re-running the same import is a no-op.

Step 1c — The knowledge commons (already on in standard)

If you have no runbooks to import either, the knowledge commons covers the gap — a shared, vendor-neutral bundle of generic playbooks (CrashLoopBackOff, unbound PVCs, stuck cert-manager challenges, unschedulable pods) mounted as a second, read-only catalog root.

The standard and full profiles enable it for you, so if you installed with one of those, skip ahead — this step is already done. Add the block yourself only when starting from minimal or the chart defaults, or when you want to pin the corpus:

catalog:
  dir: /var/lib/runlore/catalog          # your own catalog, from Step 1
  commons:
    url: https://github.com/Smana/runlore-kb-commons
    branch: main
    interval: 24h
    dir: /var/lib/runlore/commons        # must differ from catalog.dir

Both roots are indexed together and your own entries win scoring ties, so the commons acts as a floor and never competes with knowledge from your own incidents. The curator never writes to it.

One property to know before enabling it: commons entries never fire instant recall. They are resource-less by design, and recall’s structural filter rejects a resource-less entry for any alert carrying a workload. They ground kb_search mid-investigation instead — which is the honest role for a generic playbook. See Knowledge Commons for why that is the correct behaviour rather than a limitation.


Step 2 — GitHub App for curation (optional)

The Curator writes findings back to your KB repo: confident, verified root causes become a PR drafting an OKF entry; less-confident ones are delivered to chat only — no repo artifact (a below-bar guess must not enter the catalog). Auth is a GitHub App — the secure choice over a personal access token: fine-grained permissions, per-repo installation, and short-lived (1-hour) installation tokens minted on demand from the App’s private key (no long-lived credential in the cluster).

Create the App

  1. Settings → Developer settings → GitHub Apps → New GitHub App.

  2. Homepage URL: anything (e.g. your repo). Disable Webhooks (RunLore doesn’t receive GitHub webhooks).

  3. Repository permissions (least privilege — grant only these):

    PermissionAccessWhy
    ContentsRead & writepush the drafted OKF entry to a branch on the KB repo
    Pull requestsRead & writeopen the curation PR
    IssuesRead & writeopen knowledge-gap issues for recurring unresolved patterns (Phase-2 grooming)

    If your Flux source repos are private and you want real Git diffs from them, also grant Contents: Read-only and install the App on those repos. Public source repos need nothing.

  4. Create, then Generate a private key — download the .pem (you only see it once).

  5. Note the App ID (on the App’s page).

  6. Install App → install it on only the specific repos it needs (the KB repo, plus any private source repos) — not “All repositories”. Open the installation and note the Installation ID (the number in the install settings URL: .../installations/<id>).

Security best practices

  • Never commit the private key or put it in values.yaml. Store it in a Secret (step 3) — ideally synced from a vault via External Secrets.
  • Scope the installation to specific repos, and grant only the three write permissions above.
  • Installation tokens are already short-lived (1h) and auto-refreshed — there is no long-lived token to leak. Rotate the App private key periodically anyway.
  • Restrict who can administer the App in your org.
  • RunLore’s writes are confined to the forge — it has no cluster-mutating permissions.

Full key reference and the source-repo App-access edge case: Integrations → GitHub.


Step 3 — Credentials

Create a Secret with the credentials your config references by env-var name. In production, prefer an ExternalSecret that pulls these from your vault instead of kubectl create secret.

kubectl -n runlore create secret generic runlore-secrets \
  --from-literal=ANTHROPIC_API_KEY='<model-api-key>' \
  --from-literal=RUNLORE_WEBHOOK_TOKEN="$(openssl rand -hex 32)" \
  --from-literal=SLACK_WEBHOOK_URL='https://hooks.slack.com/services/...' \
  --from-file=GITHUB_APP_PRIVATE_KEY=/path/to/app-private-key.pem

On an OpenAI-compatible endpoint the key is OPENAI_API_KEY instead (omit it entirely when the endpoint is keyless), and values-full.yaml adds one more: APPROVAL_TOKEN, which gates the action approval and kill-switch endpoints.

RUNLORE_WEBHOOK_TOKEN is required once a model is configured. The serve path fails closed — it refuses to start with an anonymous alert webhook when an LLM is wired (the webhook’s labels/annotations flow into the prompt and bill the model), so set config.server.webhook_token_env to this key. See Step 5.

Only include the keys you use. The chart injects the whole Secret as env vars via envFrom, and the config references each by its env-var name (api_key_env, webhook_url_env, private_key_env, …). Wiring Matrix, a generic webhook, or another notifier instead of Slack follows the exact same pattern — see the notifier’s own page under Integrations for its env-var names.


Step 4 — Configure and install

RunLore installs in-cluster with one Helm command (this is the recommended deployment). You give it a values.yaml and run helm install — pick one of the three shipped profiles below rather than writing one from scratch, or jump to Install if you just want the command.

Pick a profile

The chart ships three profiles, each a valid install on its own. Start at the one that matches what you have wired, and step up later by copying blocks from the next:

ProfileWhat it wiresStart here if
values-minimal.yamlAlert webhook, a model, Slack. Read-only, ~15 lines.You want to see it react to a real alert today.
values-standard.yaml+ git-synced knowledge catalog, GitHub App curation, metrics + logs evidence, JSON logs and /metrics.You did steps 1–2 and want the learning loop.
values-full.yaml+ leader-elected HA, persistence, a default-deny NetworkPolicy, the approve-rung action ladder, AWS cloud + network-flow signals, instant recall.You are hardening a production install.

All three are validated in CI against the chart’s values.schema.json and the agent’s config schema, so a block you copy out of one is known to load.

A values file is not a runlore.yaml. Everything the agent itself reads is nested under config: — the chart unwraps that block verbatim into the mounted runlore.yaml. The sibling top-level keys (replicaCount, catalog, persistence, rbac, networkPolicy…) are chart-level and never reach the agent. The per-integration pages under Integrations show raw runlore.yaml snippets: nest them under config: when pasting into a values file.

Start: values-minimal.yaml

This is the whole file — investigate and notify, nothing else. It references the Secret from step 3 by env-var name:

replicaCount: 1              # one worker; values-full.yaml adds leader-elected HA
image:
  tag: ""                    # defaults to the chart appVersion; pin it in production
envFrom:
  - secretRef:
      name: runlore-secrets  # ANTHROPIC_API_KEY, RUNLORE_WEBHOOK_TOKEN, SLACK_WEBHOOK_URL
config:
  gitops:
    engine: flux             # or "argocd" — stated because the default is flux either way
  sources:
    alertmanager: {}         # presence is enablement — accept the alert webhook
  model:
    provider: anthropic      # or `openai` + base_url for any OpenAI-compatible endpoint
    model: claude-sonnet-5
    api_key_env: ANTHROPIC_API_KEY
  server:
    webhook_token_env: RUNLORE_WEBHOOK_TOKEN  # MANDATORY once a model is set (serve fails closed)
  notify:
    slack:
      webhook_url_env: SLACK_WEBHOOK_URL

Everything left out is genuinely optional: an unset data source disables the tool it would have unlocked, and an unset forge disables curation. Swap the model block for any of the LLM providers, and the notify block for Matrix, a webhook, or a templated payload.

Step up: standard and full

values-standard.yaml adds the half that makes RunLore more than a one-shot investigator: catalog.gitSync: true plus config.catalog.git (the KB repo from step 1, re-pulled on an interval so merged PRs flow back into what the agent searches), config.forge with the GitHub App from step 2, and the metrics + logs endpoints the investigation queries for evidence. It also turns on JSON logging and the /metrics endpoint with a VMServiceScrape.

values-full.yaml is the annotated reference for a hardened install — read it top to bottom rather than pasting it, since its cloud and network blocks describe one concrete environment (EKS + Cilium). It adds: 2 replicas with leader election (only the leader investigates), a StatefulSet + PVC so the outcome ledger and the hash-chained audit log survive restarts, networkPolicy.strict with an explicit egress allowlist and ingress scoped to your monitoring namespace, actions.mode: approve (envelope-filtered remediations that execute only after a human click — see Design), the AWS cloud control plane and a network-flow signal, and catalog.instant_recall — which short-circuits the LLM loop entirely when the catalog already holds a trustworthy answer.

Every key either profile sets is documented in Configuration, and the chart’s own values.yaml carries the exhaustive, commented list of chart-level knobs.

Install

Save your chosen profile as values.yaml, edit the placeholders, and deploy with a single command — the chart is an OCI artifact on GHCR, published on every release:

helm install runlore oci://ghcr.io/smana/charts/runlore -n runlore --create-namespace -f values.yaml

Pin a version with --version X.Y.Z (the chart version tracks the RunLore release). Dev alternative — working from a clone of this repo, install from the chart path instead: helm install runlore deploy/helm/runlore -n runlore --create-namespace -f values.yaml.

Every published chart is cosign keyless-signed. To verify before installing (optional):

cosign verify ghcr.io/smana/charts/runlore:<version> \
  --certificate-identity-regexp 'https://github.com/Smana/runlore/\.github/workflows/release-chart\.yml@.*' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com

Step 4b — AWS cloud provider (optional)

Enables the cloud_what_changed (CloudTrail) and cloud_resource_health (EC2/ASG/EKS) tools so the agent can see infra changes that never touched GitOps. Read-only, authenticated with in-cluster identity — no static AWS keys. Full config, the read-only IAM policy, and how it binds to the ServiceAccount (EKS Pod Identity or IRSA): Integrations → AWS cloud control plane.

Cilium clusters only — the EKS Pod Identity credential endpoint runs on the node host network (169.254.170.23:80), which Cilium classifies as the host entity, so a plain Kubernetes NetworkPolicy cannot match it and the credential fetch is silently dropped. Set networkPolicy.awsPodIdentity: true (chart value) to render a CiliumNetworkPolicy that allows it — see that page’s Notes for the exact YAML and the hubble observe … DROPPED command that confirms this is the issue you’re hitting.

Under networkPolicy.strict: true, that flag is not enough. It covers only the credential fetch; the AWS API calls themselves (CloudTrail, EKS, EC2, STS — public regional endpoints on 443) are ordinary egress and are dropped unless you declare them too, leaving both cloud tools silently empty. values-full.yaml shows the networkPolicy.extraEgress rule and how to pin the CIDR (PrivateLink endpoint subnets, or your NAT gateway EIP).


Step 5 — Point Alertmanager at the webhook

RunLore reacts to Alertmanager’s webhook. Route the alerts you care about to its Service (the trigger policy in config is the real filter — Alertmanager routing is just the firehose):

# alertmanager config
receivers:
  - name: runlore
    webhook_configs:
      - url: http://runlore.runlore.svc:8080/webhook/alertmanager
route:
  routes:
    - receiver: runlore
      continue: true        # keep your existing routing too

With 2+ replicas, every warm replica is Ready — the Service may route a webhook to any of them, and a non-leader replica transparently proxies it to the elected leader (single hop, looked up via the leader-election Lease), so only the leader’s queue ever investigates.

Harden for production

Once a model is configured the webhook token is mandatory (the serve path fails closed — see Step 3); the chart’s NetworkPolicy ingress, however, is permissive by default (ingressFrom: [] ⇒ any source), so lock that down before pointing a real alert stream at it:

  1. Require a bearer token. Name an env var in server.webhook_token_env (wired from your Secret); unauthenticated requests are then rejected with 401. This token is required whenever a model is configured (and therefore also under actions.mode=auto) — serve refuses to start without it.
    # values.yaml
    config:
      server:
        webhook_token_env: RUNLORE_WEBHOOK_TOKEN
    # alertmanager — send the same token as a bearer credential
    webhook_configs:
      - url: http://runlore.runlore.svc:8080/webhook/alertmanager
        http_config:
          authorization:
            type: Bearer
            credentials_file: /etc/alertmanager/secrets/runlore-webhook-token
  2. Scope ingress to only the namespace that should reach the webhook (e.g. your monitoring stack):
    # values.yaml — spliced into the NetworkPolicy `from:`
    networkPolicy:
      ingressFrom:
        - namespaceSelector:
            matchLabels: { kubernetes.io/metadata.name: monitoring }

See the Security model for the full posture — redaction, RBAC, the action gate.


Step 6 — Verify

# every replica becomes Ready once its catalog is warm; the Lease names the leader
# (holder identity is <podName>_<podIP> — the IP lets standbys forward work to it)
kubectl -n runlore get pods
kubectl -n runlore get lease runlore-leader -o jsonpath='{.spec.holderIdentity}'; echo

# startup wiring
kubectl -n runlore logs deploy/runlore | grep -E 'catalog loaded|using LLM investigator|curator enabled|watching gitops failures|serving'

Expected lines: catalog loaded … entries=N, using LLM investigator, watching gitops failures, curator enabled (if configured), runlore serving.

Fire a test: trigger a critical/prod alert (or flux suspend+break a Kustomization). You should see msg=incident … investigate=truemsg=findings …, a message in Slack, and (if curation is on) msg=curated url=… pointing at a PR/issue on your KB repo.


Step 7 — The Learn loop: KB lifecycle & re-runs

When curation is on, each investigation lands in your KB repo with a lifecycle label so you can tell raw findings from vetted knowledge:

  • triggered — RunLore just opened this issue/PR; a raw finding, not yet worked.
  • investigating — being worked (RunLore sets this when you ask it to re-run; see below).
  • solved — root cause confirmed and the resolution captured. Only solved entries with a written resolution should be merged as a reusable Playbook — that’s the quality gate that keeps the catalog trustworthy.
  • wontfix — closed without a Playbook.

(High-confidence findings open as a PR drafting an OKF entry; lower-confidence ones open as an issue to triage.)

Re-run an investigation on demand. RunLore takes no inbound GitHub webhooks, so it polls: add the reinvestigate label to one of its curated issues and within a couple of minutes it re-runs the investigation (building on the captured context), posts the fresh findings as a comment, and moves the label to investigating. Use it after more has happened, or once you’ve added a relevant Playbook and want a sharper answer. Only RunLore-originated issues (carrying the runlore label) are eligible.


What RunLore can and cannot do

  • Cluster: read-only by default — it reads Flux/Argo resources, metrics (PromQL), logs (LogsQL), and network flows (Hubble), and never writes.
  • Cloud (AWS, optional): read-only — CloudTrail LookupEvents + EC2/ASG/EKS Describe, via in-cluster identity (EKS Pod Identity / IRSA). No mutating cloud calls exist in the code. RBAC is limited to watching those resources + its own leader-election Lease. With actions.mode: approve + rbac.allowActions: true, it can execute reversible Flux ops (suspend/resume/reconcile) only after explicit human approval — either POST /actions/<id>/approve (token-gated) or Slack Approve/Reject buttons (enable Slack Interactivity with Request URL …/slack/interactions and set slack.signing_secret_env; clicks are HMAC-verified). The envelope is re-checked at execution and every action is audit-logged.
  • Unattended (actions.mode: auto): executes eligible actions with no human in the loop — but only reversible ops, only above min_confidence, rate-limited, and instantly haltable via POST /actions/pause (/resume). Start with dry_run: true, scope which incidents it acts on via the trigger policy, and watch the audit log + delivered summary. Irreversible actions are never auto-run.
  • Forge: writes issues/PRs to the one KB repo you configure, via the scoped GitHub App.
  • Secrets: referenced by env-var name from a Secret you control; nothing is inlined.

Next

  • Integrations — every trigger, LLM, data source, notifier, and forge RunLore plugs into, each with a minimal config and a local-verification recipe.
  • CLI reference — the full lore demo / lore investigate flag and env-var reference from the tiers above.
  • Configuration — every config key, organized by subsystem.
  • Troubleshooting — why an investigation didn’t start, timed out, or didn’t file a PR.
  • Security model — read-only posture, redaction, RBAC, the action gate.
  • Upgrade & uninstallhelm upgrade/uninstall, what persists, and cleanup.
  • Design — architecture and the autonomy ladder.
  • CONTRIBUTING.md — run the full feature suite locally on k3d.