| title | LLM incident walkthrough | |||
|---|---|---|---|---|
| description | Triage a failed backup with the built-in LLM helper and ship a signed evidence bundle to the audit log. | |||
| tags |
|
Walks through using
pg_hardstorage llmto triage a real incident: a backup that fails because the replication slot is missing. You drop into theincidentskill, walk through the assistant's response, and export a signed evidence bundle of the entire session. About 15 minutes.
The LLM helper is a read-only triage assistant, not an autopilot.
The default execution mode is read-only: the assistant can run
read_* and list_* tools to inspect cluster state, but
execute_command refuses every invocation. Remediation is always the
operator's call. Every prompt, tool call, response, and exit is
audit-logged with a hash chain so you can show your auditor exactly
what was inspected.
- A working
pg_hardstorageinstall (see getting-started) and a sandbox repo atfile:///tmp/hs-llm-repo. - An OpenAI-compatible LLM endpoint or a local Ollama at the
default
:11434. The default provider isopenaiwhen an API key is in scope; otherwise the CLI refuses withllm.no_provider_configuredand prints the exact env vars to set. Pass--provider mockfor the canned-reply path used by this walkthrough's plumbing checks.
For a fully local setup with Ollama:
ollama pull llama3.1:8b
export PG_HARDSTORAGE_URL=http://127.0.0.1:11434/v1 # Ollama's OpenAI-compat endpoint
export PG_HARDSTORAGE_LLM_KEY=ollama # any non-empty value
export PG_HARDSTORAGE_LLM_MODEL=llama3.1:8b
pg_hardstorage llmFor OpenAI:
export PG_HARDSTORAGE_LLM_KEY='sk-proj-...'
# PG_HARDSTORAGE_LLM_MODEL defaults to gpt-4o-mini; override
# if your account uses a different model.
pg_hardstorage llmFor the mock provider (no network, deterministic answers, useful for this walkthrough):
export PG_HARDSTORAGE_LLM_PROVIDER=mockSee Configure the LLM helper and the
annotated reference at share/pg_hardstorage.sample.yaml for the
full configuration surface (Anthropic via OpenAI-compat, Azure
OpenAI, OpenRouter, vLLM, ...).
The fastest way to make a backup fail is to point it at a healthy PostgreSQL but block the replication slot. Start a sandbox PG and take one good backup so the deployment exists:
docker run -d --name hs-llm-pg -e POSTGRES_PASSWORD=postgres \
-p 5432:5432 postgres:17
pg_hardstorage repo init file:///tmp/hs-llm-repo
pg_hardstorage init \
--pg-connection "${PG_CONNECTION:-postgres://postgres:postgres@127.0.0.1/postgres}" \
--repo file:///tmp/hs-llm-repo \
--deployment db1 \
--yesNow drop the slot from underneath the agent (this is the same shape
as a Patroni-failover or a curious DBA running
SELECT pg_drop_replication_slot(...)):
PGPASSWORD=postgres psql -h 127.0.0.1 -U postgres -c \
"SELECT pg_drop_replication_slot('pg_hardstorage_db1');"pg_hardstorage wal stream db1 \
--pg-connection "${PG_CONNECTION:-postgres://postgres:postgres@127.0.0.1/postgres}" \
--repo file:///tmp/hs-llm-repoERROR: WAL stream replication slot 'pg_hardstorage_db1' is not present on the server.
What to do: the slot was probably dropped by an admin. Recreate it
with:
pg_hardstorage wal repair db1The exit code is non-zero and the structured event ID is
wal_gap_detected. You could pg_hardstorage wal repair blindly —
or you can ask the helper to talk you through it.
pg_hardstorage llm chat --audit-repo file:///tmp/hs-llm-repoThe --audit-repo flag captures
every llm.* event into the named repo's audit chain so you can
export a signed evidence bundle later.
[AI assistant — verify every suggestion before running]
skill: incident v1.0.0 · provider: mock · model: (provider default)
url: (provider default)
/help for commands · /exit to quit
(mock provider — replies are stub echoes; pass --provider openai for a real model)
> The model: and url: lines show what the chat is
actually talking to. When colour output is supported
(real TTY, NO_COLOR not set, TERM not dumb) the
field labels render in cyan and values in bold; piping
the banner to a file or less produces plain text.
> The wal stream for db1 just failed with "replication slot is not
> present on the server". Walk me through what to do.
A response from a real provider will:
- Call
read_doctorto check the deployment's health snapshot. - Call
read_status db1to confirm last-known WAL LSN. - Match the
wal_gap_detectedevent class against the runbook index and surface R6 — Slot dropped, gap detected. - Suggest
pg_hardstorage wal repair db1and explain the side-effects (the repaired slot bootstraps from the latest committed segment; any LSN gap is recorded as awal_gapaudit event).
The helper does not run wal repair. It surfaces it and stops.
The slash commands print exactly what the assistant has access to:
> /show-skill
incident · v1.0.0 · /home/you/.config/pg_hardstorage/skills/incident.skill.yaml
> /show-tools
read_doctor (read-only, preloaded)
read_status (read-only, preloaded)
list_deployments (read-only, preloaded)
list_backups
read_backup
read_repo_usage
read_audit
list_runbooks
read_runbook
search_docs
suggest_command
preview_command
> /show-budget
tokens used: 4128 · budget: 120000 · 116000 remaining
execute_command is not in the list — the skill's
permissions.read_only: true means the runner refuses every mutation
even if the model asks. This is the design.
Leave the chat open with Ctrl-Z (or /exit to close) and run the
suggestion in another terminal:
pg_hardstorage wal repair db1 \
--pg-connection "${PG_CONNECTION:-postgres://postgres:postgres@127.0.0.1/postgres}" \
--repo file:///tmp/hs-llm-repoConfirm the slot is back:
pg_hardstorage doctor db1db1 — PG 17.x — primary @ 127.0.0.1
✓ PostgreSQL reachable
✓ Replication slot 'pg_hardstorage_db1' active
✗ WAL gap recorded: [0/1A000000 .. 0/1B000000]
Suggested fix: pg_hardstorage wal repair db1 --repo <url> --pg-connection <conn>The recorded gap is the exact range during which the slot was
absent. You can chase it with wal audit or accept it as a known
recovery limitation depending on your RPO target.
Note the session ID printed in the chat header (session=01HXX...)
and pass it to llm export-session:
pg_hardstorage llm export-session 01HXX... \
--repo file:///tmp/hs-llm-repo \
--out /tmp/hs-llm-incident.tar.gz✓ exported 14 audit events
✓ Merkle root anchored: sha256:c1aa...
✓ wrote /tmp/hs-llm-incident.tar.gz (signed, ed25519)Inside the tarball:
session.json # session metadata (skill, provider, principal)
events.ndjson # every llm.* event in order, hash-chained
manifest.json # bundle index + audit chain head
manifest.sig # ed25519 signature over manifest.json
This is the artefact you hand to the auditor: what the operator was told, by which model, with which tools available, and what the operator chose to do with that advice.
docker rm -f hs-llm-pg
rm -rf /tmp/hs-llm-repo /tmp/hs-llm-incident.tar.gzYou used the LLM helper as a triage cone — narrowing "WAL stream failed" to a specific runbook with the cluster's actual state in scope, without granting the model any mutation power. Every turn was captured into the audit chain, and the export step bundled it into a portable signed artefact.
Three things to internalise:
read-onlyis the default for a reason.advise+executeexists for tightly-scoped automation, but every gate (n-of-m approval, insider-threat scan, audit chain) is still in the path.- Skills are configurable. Drop a YAML under
<config>/skills/<name>.skill.yamlandpg_hardstorage llm skill installit; the precedence chain (operator overlay > tenant > builtin) applies. auto_on_errorcan launch the matching skill the instant a structured error fires. Set--on-error-llmonce orPG_HARDSTORAGE_ON_ERROR_LLM=1in the agent's env, and the nextwal_gap_detectedopens an incident chat directly.
- R6 — Slot dropped, gap detected — the runbook the helper surfaced.
- Operator guide — Troubleshooting — the quick-reference index of error → fix.
- Architecture tour — where the LLM helper sits in the data plane.