loki-mode 8.5.2 → 8.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,102 @@
1
+ # Environment variables
2
+
3
+ The operator-facing knobs, with defaults read from the source rather than from
4
+ memory. Every variable on this page is asserted by a test to still exist, so
5
+ this document cannot quietly rot into fiction.
6
+
7
+ ## What this page is not
8
+
9
+ The codebase references a few hundred `LOKI_*` tokens. Most are internal
10
+ plumbing between the CLI and the runner (`LOKI_REG_TARGET`,
11
+ `LOKI_CI_JSON_FINDINGS`, and similar), or prefix fragments that are never a
12
+ whole variable. Publishing that number as an "operator surface" would be
13
+ misleading, so this page covers only what an operator would deliberately set.
14
+
15
+ `loki config schema` lists 64 more keys that map to environment variables and
16
+ can be written to `.loki/config.yaml`. Those are not repeated here. The
17
+ variables below are the ones **not** in that schema -- historically the ones
18
+ with nowhere to look them up.
19
+
20
+ ## Running a build
21
+
22
+ | Variable | Default | Effect |
23
+ |---|---|---|
24
+ | `LOKI_PRD_FILE` | none | Path to a spec file, as an alternative to the positional argument. |
25
+ | `LOKI_MAX_ITERATIONS` | `1000` | Hard cap on iterations. Reaching it is a deterministic terminal failure, not a success. Also in `config schema`. |
26
+ | `LOKI_BUDGET_LIMIT` | unset (no cap) | Spend cap in USD. On exhaustion the run stops and reports a terminal failure -- see [exit codes](./exit-codes.md). |
27
+ | `LOKI_MAX_DURATION` | unset (no cap) | Wall-clock cap in seconds. Stops cleanly at the next iteration boundary with a `max_duration_reached` terminal status. `loki start --max-duration` also accepts `90m` / `2h`. |
28
+ | `LOKI_AUTO_CONFIRM` | unset | `true`/`false` to control prompts. Takes precedence over `CI`. |
29
+ | `LOKI_CONFIG_DUMP` | `0` | `1` prints the resolved configuration and exits **without starting a run or spending anything**. |
30
+
31
+ For a cost estimate before committing to a run, `loki plan <spec> --json` is
32
+ the better tool: it reports complexity, iterations, tokens and cost without
33
+ executing. See [cost controls](./cost-controls.md) for how the three caps
34
+ interact and why hitting one is a failure rather than a success.
35
+
36
+ ## Choosing a model and provider
37
+
38
+ | Variable | Default | Effect |
39
+ |---|---|---|
40
+ | `LOKI_PROVIDER` | `claude` | `claude`, `cline`, `codex`, or `aider`. |
41
+ | `LOKI_MAX_TIER` | unlimited | Caps model tier, so a run cannot escalate past what you are willing to pay for. |
42
+ | `LOKI_TIER` | per-phase default | Forces a specific tier for the run. |
43
+ | `LOKI_SESSION_MODEL` | provider default | Pins the session model explicitly. |
44
+ | `LOKI_MODEL_OVERRIDE` | unset | Overrides the resolved model outright. |
45
+
46
+ `LOKI_MAX_TIER` is the cost control worth knowing: it bounds escalation, while
47
+ `LOKI_BUDGET_LIMIT` bounds total spend. They answer different questions and are
48
+ usefully set together.
49
+
50
+ ## Output volume
51
+
52
+ | Variable | Default | Effect |
53
+ |---|---|---|
54
+ | `LOKI_LOG_LEVEL` | `info` | `debug`, `info`, `warn`, or `error`. |
55
+ | `LOKI_QUIET` | `0` | `1` is shorthand for `warn`. |
56
+
57
+ **Errors are never suppressed.** `error` is the floor, so even the quietest
58
+ setting still prints failures -- a verbosity control that could hide why a
59
+ build failed would be a footgun. An unrecognized value falls back to `info`
60
+ rather than silencing the run, so a typo in a pipeline config cannot blind you.
61
+
62
+ Available as flags too: `loki start --quiet`, `loki start --log-level LEVEL`.
63
+
64
+ Decorative output (the HUD, the completion card, the start headline) already
65
+ suppresses itself when stdout is not a TTY, so CI logs were never the wall of
66
+ banners you might expect. These variables control the remaining `[INFO]` and
67
+ `[STEP]` lines.
68
+
69
+ ## Platform integration
70
+
71
+ | Variable | Default | Effect |
72
+ |---|---|---|
73
+ | `LOKI_DURABLE_STATE` | `0` | `1` enables durable state **and** the richer process-exit contract that lets Kubernetes distinguish a deterministic failure from a crash. See [exit codes](./exit-codes.md). |
74
+ | `LOKI_SDK_LOOP` | unset | Routes the run through the Bun/TypeScript runner instead of bash. Both implement the same exit contract. |
75
+
76
+ `LOKI_DURABLE_STATE=1` is the one to set in a Job or task definition. Without
77
+ it every failure collapses to exit 1 and the platform cannot tell "re-running
78
+ this is pointless" from "this crashed and should resume".
79
+
80
+ ## Precedence
81
+
82
+ Command-line flags beat environment variables, which beat `.loki/config.yaml`,
83
+ which beats `~/.config/loki-mode/config.yaml`.
84
+
85
+ To see what actually resolved, without starting a run:
86
+
87
+ ```sh
88
+ LOKI_CONFIG_DUMP=1 loki start ./prd.md
89
+ ```
90
+
91
+ ## Setting these persistently
92
+
93
+ `loki config set` writes to the config file for the keys it supports:
94
+
95
+ ```sh
96
+ loki config set budget 25
97
+ loki config set provider claude
98
+ loki config set maxTier sonnet
99
+ ```
100
+
101
+ `loki config schema` lists every key the config file understands, and
102
+ `loki config show` prints the current effective values.
@@ -0,0 +1,130 @@
1
+ # Exit codes
2
+
3
+ Every code below was read from the source, not from a help text. If a command
4
+ is absent from this page it returns only the shell defaults (0 on success,
5
+ nonzero on failure) and you should not build a gate on anything finer.
6
+
7
+ ## The one rule
8
+
9
+ **Severity rises with the code.** `[ $rc -ge 2 ]` always means "worse than the
10
+ level-1 outcome" for any command on this page. If you remember nothing else,
11
+ remember that a bigger number is never better news.
12
+
13
+ ## `loki start`
14
+
15
+ Two contracts, and which one you get depends on an environment variable. This
16
+ is the part most likely to surprise a script author.
17
+
18
+ ### Default (local, CI)
19
+
20
+ | Code | Meaning |
21
+ |---|---|
22
+ | 0 | The run completed |
23
+ | nonzero | Something went wrong |
24
+
25
+ There is no finer signal. A gate that needs to tell "failed the quality gate"
26
+ from "crashed" must opt into the durable contract below.
27
+
28
+ ### With `LOKI_DURABLE_STATE=1` (the platform contract)
29
+
30
+ Written for a Kubernetes Job, an ECS task, or a systemd unit that has to decide
31
+ whether retrying is worth anything. The distinction it draws is **will running
32
+ this again produce a different result**.
33
+
34
+ | Code | Meaning | Should the platform retry? |
35
+ |---|---|---|
36
+ | 0 | Completed, or a human stopped it (council approved, completion promise, force-stop, paused, interrupted, stopped) | No. It is done, or a person is driving. |
37
+ | 20 | Deterministic terminal failure (failed a gate, max iterations, max retries, **budget exceeded**, **wall-clock cap reached**, policy blocked, contradictory spec) | **No.** The same inputs fail the same way; a retry only spends money to arrive here again. |
38
+ | any other nonzero | Crash (SIGKILL, eviction, node loss) | Yes. The restarted run resumes from the durable volume. |
39
+
40
+ `budget_exceeded` sits with the failures deliberately. It used to exit 0 on the
41
+ reasoning that a human would raise the cap and resume, which is true at a
42
+ terminal and false inside a Job: there is no human, so a build stopped mid-work
43
+ by the cost breaker was reported as a success. Exit 0 must mean the work is
44
+ finished or a person chose to stop it.
45
+
46
+ Helm wires this up for you: `worker.exitCodes.terminalFailure` (default 20)
47
+ feeds the Job's `podFailurePolicy`, so a deterministic failure fails the Job
48
+ immediately instead of burning `backoffLimit`. Requires Kubernetes 1.31+.
49
+
50
+ Both the bash runner and the Bun runner (`LOKI_SDK_LOOP`) implement this
51
+ identically; a parity test asserts they agree status for status.
52
+
53
+ ## `loki verify`
54
+
55
+ | Code | Verdict |
56
+ |---|---|
57
+ | 0 | VERIFIED |
58
+ | 1 | CONCERNS (findings below the block threshold, or inconclusive evidence) |
59
+ | 2 | BLOCKED (findings at or above the block threshold) |
60
+ | 3 | Verifier error: it could not complete, and never silently passes |
61
+
62
+ Code 3 matters more than it looks. A verifier that cannot run is not a pass,
63
+ so `[ $rc -eq 0 ]` is the only safe test for "verified" -- `[ $rc -ne 2 ]`
64
+ would treat a broken verifier as acceptable.
65
+
66
+ An early draft spec listed `1=BLOCKED, 2=CONCERNS`. That ordering was rejected:
67
+ it is not used anywhere, it has no consumers, and it inverts the
68
+ severity-rises-with-the-code rule that every other command follows.
69
+
70
+ ## `loki ci`
71
+
72
+ | Code | Meaning |
73
+ |---|---|
74
+ | 0 | Passed, or all findings are below `--fail-on` |
75
+ | 1 | Findings exceed the `--fail-on` threshold |
76
+ | 2 | Error: missing tools or invalid arguments |
77
+
78
+ Machine-readable output is `--format json` here, not `--json`.
79
+
80
+ ## `loki doctor`
81
+
82
+ | Code | Meaning |
83
+ |---|---|
84
+ | 0 | Every required check passed. Optional warnings do not fail the command. |
85
+ | nonzero | At least one required check failed; the output names which |
86
+
87
+ Safe as a preflight gate in an init container or pipeline step:
88
+
89
+ ```sh
90
+ loki doctor || { echo "host is not ready"; exit 1; }
91
+ ```
92
+
93
+ An absent optional provider CLI is a warning, not a blocker, so this will not
94
+ refuse to start over a tool you were never going to use.
95
+
96
+ ## Security scan
97
+
98
+ | Code | Meaning |
99
+ |---|---|
100
+ | 0 | No high or critical findings |
101
+ | 1 | At least one HIGH |
102
+ | 2 | At least one CRITICAL |
103
+
104
+ ## Signals
105
+
106
+ `loki start` handles the usual terminating signals conventionally: 130 for
107
+ SIGINT (Ctrl-C), 143 for SIGTERM. Under the durable contract these are crashes
108
+ in the retryable sense -- a pod terminated by the scheduler resumes rather than
109
+ being treated as a completed build.
110
+
111
+ ## Writing a gate
112
+
113
+ ```sh
114
+ # Block a merge unless verification is clean. Note -ne 0, not -eq 2:
115
+ # a verifier ERROR (3) must not pass.
116
+ loki verify || { echo "not verified"; exit 1; }
117
+
118
+ # Kubernetes: let the platform decide whether to retry.
119
+ LOKI_DURABLE_STATE=1 loki start ./prd.md
120
+ rc=$?
121
+ case $rc in
122
+ 0) echo "complete" ;;
123
+ 20) echo "terminal failure -- fix the spec or raise the budget, then re-submit" ;;
124
+ *) echo "crashed (rc=$rc) -- resume is safe" ;;
125
+ esac
126
+ ```
127
+
128
+ Do not read an exit code through a pipe. `$?` after a pipeline reports the last
129
+ stage, so `loki verify | tee log` gives you `tee`'s status and always looks
130
+ successful. Use `${PIPESTATUS[0]}` in bash, or capture first and test after.
@@ -0,0 +1,124 @@
1
+ # Verifying the container image
2
+
3
+ Everything on this page is a command you run against the public registry and
4
+ the public sigstore transparency log. None of it requires an account with us,
5
+ and none of it trusts anything we say here: if a command below fails, the
6
+ correct conclusion is that the image is not what we claim, not that the
7
+ instructions are stale.
8
+
9
+ ## What is covered, and from which version
10
+
11
+ Read this section before the commands. Provenance that claims more than it
12
+ delivers is worse than none, because it is exactly what an auditor will test.
13
+
14
+ | Artifact | Signature | SBOM |
15
+ |---|---|---|
16
+ | Container image (`asklokesh/loki-mode`) | first release after v8.5.2 | first release after v8.5.2 |
17
+ | npm tarball (`loki-mode`) | v7.4.10 and later | v7.4.10 and later |
18
+
19
+ **Images published up to and including v8.5.2 are not signed and have no SBOM.** A signing
20
+ workflow existed from v7.4.10 but was triggered on `release: published`, and
21
+ because our releases are created by a workflow using the default
22
+ `GITHUB_TOKEN`, GitHub never emitted that event. The workflow therefore never
23
+ ran on a real release, which the registry confirms: no `sha256-*.sig` tags
24
+ existed for any version through v8.5.2. Signing now runs inside the same job
25
+ that pushes the image, so it cannot be skipped by a trigger that does not fire.
26
+
27
+ We are not backfilling signatures for older tags. A signature applied today by
28
+ a different pipeline than the one that built the artifact months ago attests
29
+ to far less than it appears to, and the appearance is the dangerous part.
30
+
31
+ ## Verify the signature
32
+
33
+ Requires [cosign](https://github.com/sigstore/cosign) v2.x. Substitute your
34
+ version for `8.6.0`.
35
+
36
+ Signatures are bound to the image **digest**, not to a tag. Tags are mutable --
37
+ `latest` moves every release, and even a version tag can be repointed -- so
38
+ resolve the digest first and verify that.
39
+
40
+ ```sh
41
+ VERSION=8.6.0 # or whichever release first carried signing; see the table above
42
+ DIGEST=$(docker buildx imagetools inspect "asklokesh/loki-mode:${VERSION}" \
43
+ --format '{{json .Manifest.Digest}}' | tr -d '"')
44
+ echo "$DIGEST"
45
+
46
+ cosign verify \
47
+ --certificate-identity-regexp 'https://github.com/asklokesh/loki-mode/.github/workflows/release.yml@.*' \
48
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
49
+ "asklokesh/loki-mode@${DIGEST}"
50
+ ```
51
+
52
+ The two `--certificate-*` flags are the part that matters, and omitting them
53
+ is the most common way to get a meaningless pass. Keyless signing means anyone
54
+ with a GitHub account can produce a valid sigstore signature over our image;
55
+ what makes the signature *ours* is that it was issued to our workflow identity
56
+ by GitHub's OIDC issuer. Without those flags cosign will happily confirm that
57
+ the image was signed by somebody.
58
+
59
+ A pass prints a JSON block including the certificate's subject, which should
60
+ name `release.yml` in this repository.
61
+
62
+ ## Verify and read the SBOM
63
+
64
+ The SBOM is attached to the image as a CycloneDX attestation. Note this is a
65
+ *separate* artifact from the npm SBOM published on the GitHub Release: the
66
+ image carries an OS layer, a Python runtime and system packages that the npm
67
+ tarball's SBOM never described. If you are deploying by Helm, ECS, or any
68
+ other image-based path, the image SBOM is the one that matches what you run.
69
+
70
+ ```sh
71
+ cosign verify-attestation \
72
+ --type cyclonedx \
73
+ --certificate-identity-regexp 'https://github.com/asklokesh/loki-mode/.github/workflows/release.yml@.*' \
74
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
75
+ "asklokesh/loki-mode@${DIGEST}" \
76
+ | jq -r '.payload' | base64 -d | jq '.predicate' > image-sbom.cdx.json
77
+
78
+ jq '.components | length' image-sbom.cdx.json
79
+ ```
80
+
81
+ `verify-attestation` checks the signature over the SBOM before you read it,
82
+ which is the point: an SBOM you downloaded unverified tells you what someone
83
+ wanted you to believe is in the image.
84
+
85
+ **Scope limit worth knowing.** We publish a multi-arch image (linux/amd64 and
86
+ linux/arm64). The signature covers the manifest list, so it covers both
87
+ architectures. The SBOM does not: it is generated from a single resolved
88
+ platform, so package versions specific to the other architecture may differ
89
+ from what it lists. If you deploy on arm64 and need an exact bill of materials
90
+ for that architecture, generate it against the arch-specific digest yourself:
91
+
92
+ ```sh
93
+ syft "asklokesh/loki-mode:${VERSION}" --platform linux/arm64 -o cyclonedx-json
94
+ ```
95
+
96
+ Feed `image-sbom.cdx.json` to Grype, Trivy, Dependency-Track or any other
97
+ CycloneDX consumer for CVE scanning.
98
+
99
+ ## Air-gapped environments
100
+
101
+ Both commands above reach the public sigstore infrastructure (Rekor and
102
+ Fulcio) to check the transparency log. To verify inside an air-gapped network,
103
+ mirror the trust root and the artifacts on a connected host first:
104
+
105
+ ```sh
106
+ cosign save "asklokesh/loki-mode@${DIGEST}" --dir ./loki-image-bundle
107
+ cosign initialize --mirror <your-tuf-mirror> --root <your-root.json>
108
+ ```
109
+
110
+ Then transfer `loki-image-bundle/` and verify with `cosign verify --local-image
111
+ ./loki-image-bundle`. See `loki doctor --airgap` for the full host inventory
112
+ the engine itself needs at runtime.
113
+
114
+ ## If verification fails
115
+
116
+ Do not deploy the image. In order of likelihood:
117
+
118
+ 1. **You verified a tag instead of a digest.** Re-resolve the digest.
119
+ 2. **You omitted the `--certificate-*` flags**, verified a digest that was
120
+ never ours, and got a pass or a confusing mismatch. Both flags are required.
121
+ 3. **The version is v8.5.2 or earlier.** See the coverage table above; those images
122
+ are genuinely unsigned and no command will make them verify.
123
+ 4. **The image is not what we published.** Report it at
124
+ https://github.com/asklokesh/loki-mode/issues and do not run it.