loki-mode 11.17.43 → 11.17.45
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.
- package/SKILL.md +2 -2
- package/VERSION +1 -1
- package/autonomy/lib/secure-scan.py +58 -2
- package/autonomy/loki +153 -11
- package/completions/_loki +12 -2
- package/completions/loki.bash +16 -1
- package/dashboard/__init__.py +1 -1
- package/docs/CLI-REFERENCE.md +3 -1
- package/docs/INSTALLATION.md +1 -1
- package/docs/WORKSPACES.md +1 -1
- package/docs/v10/BOARD.md +67 -37
- package/docs/v10/CLI-MODERN.md +1 -1
- package/docs/v10/PROGRESS.md +6 -0
- package/loki-ts/dist/loki.js +1185 -1139
- package/mcp/__init__.py +1 -1
- package/package.json +2 -2
- package/packages/control-plane/dist/server.js +192 -142
- package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
- package/web-app/dist/assets/{AdminPage-BKzSXu_j.js → AdminPage-BCGIu6vt.js} +1 -1
- package/web-app/dist/assets/{Avatar-DJ7ngKyG.js → Avatar-FGro9b68.js} +1 -1
- package/web-app/dist/assets/{Badge-DzYByk2v.js → Badge-DZ0OOxdx.js} +1 -1
- package/web-app/dist/assets/{Button-BxDDHZo6.js → Button-C702FTxi.js} +1 -1
- package/web-app/dist/assets/{CockpitPage-XhjPH9NC.js → CockpitPage-B1CZaaXx.js} +1 -1
- package/web-app/dist/assets/{ComparePage-do4LC1Kw.js → ComparePage-DRd-w9yO.js} +1 -1
- package/web-app/dist/assets/{ErrorBoundary-Tw_oy-Da.js → ErrorBoundary-B9T1V8_Q.js} +1 -1
- package/web-app/dist/assets/{EvidenceReceiptPanel-BWlmi-B5.js → EvidenceReceiptPanel-CScPICN7.js} +1 -1
- package/web-app/dist/assets/{GitHubIssuesPanel-CuyOWTsQ.js → GitHubIssuesPanel-BL8nzIoH.js} +1 -1
- package/web-app/dist/assets/{GitHubPRsPanel-DCCFA4Cg.js → GitHubPRsPanel-W0FX5DSl.js} +1 -1
- package/web-app/dist/assets/{HomePage-zXruJ694.js → HomePage-_3jq9WUA.js} +2 -2
- package/web-app/dist/assets/{LoginPage-D2hTXZIt.js → LoginPage-DsPxoAOJ.js} +1 -1
- package/web-app/dist/assets/{MagicPage-8tcSJtKw.js → MagicPage-nOER2-ZE.js} +1 -1
- package/web-app/dist/assets/{MetricsPage-BQbmcPiu.js → MetricsPage-CoiAlCTC.js} +1 -1
- package/web-app/dist/assets/{NotFoundPage-VVysOMpk.js → NotFoundPage-l46SOx1i.js} +1 -1
- package/web-app/dist/assets/{ProjectPage-WU7f9RkJ.js → ProjectPage-DxO8dkf_.js} +3 -3
- package/web-app/dist/assets/{ProjectsPage-DEI8Ha7d.js → ProjectsPage-DtNSAVm9.js} +1 -1
- package/web-app/dist/assets/{SettingsPage-B9NeXoBw.js → SettingsPage-DbNetRfY.js} +1 -1
- package/web-app/dist/assets/{ShowcasePage-CIRBbxd5.js → ShowcasePage-BSzhjOYS.js} +1 -1
- package/web-app/dist/assets/{SystemSettingsPage-CfuZUU6P.js → SystemSettingsPage-DWrN72dn.js} +1 -1
- package/web-app/dist/assets/{TeamsPage-DF9nzDjA.js → TeamsPage-CVFEOL9r.js} +1 -1
- package/web-app/dist/assets/{TemplatesPage-BFyWWIT4.js → TemplatesPage-IpcsZhEh.js} +1 -1
- package/web-app/dist/assets/{TerminalOutput-CYSWsLu3.js → TerminalOutput-Bv9Zgwyj.js} +1 -1
- package/web-app/dist/assets/{activity-BQ44JhWT.js → activity-CHgtGwwW.js} +1 -1
- package/web-app/dist/assets/{bell-CjAfaEDR.js → bell-Ym0Mx2Xd.js} +1 -1
- package/web-app/dist/assets/{bot-DgwH21q_.js → bot-C-6tA3JT.js} +1 -1
- package/web-app/dist/assets/{check-CHZWFNwB.js → check-CyFyZ4Qi.js} +1 -1
- package/web-app/dist/assets/{chevron-left-B7Etn49H.js → chevron-left-B3SPNdcz.js} +1 -1
- package/web-app/dist/assets/{circle-alert-nPoVqFm8.js → circle-alert-DcKKeKmd.js} +1 -1
- package/web-app/dist/assets/{clock-C-sLUpGG.js → clock-CHLwYBFX.js} +1 -1
- package/web-app/dist/assets/{cloud-D4K7FUJF.js → cloud-CBE7j7iG.js} +1 -1
- package/web-app/dist/assets/{code-xml-BZHY2-WK.js → code-xml-Cu-b65vO.js} +1 -1
- package/web-app/dist/assets/{database-CEj7r6Ao.js → database-DSJlQYxs.js} +1 -1
- package/web-app/dist/assets/{dollar-sign-DPHUDpsN.js → dollar-sign-BcPFsZq0.js} +1 -1
- package/web-app/dist/assets/{file-code-corner-CboHKdgp.js → file-code-corner-Dl_LDpv_.js} +1 -1
- package/web-app/dist/assets/{file-plus-DsA4DU-n.js → file-plus-BoLEmKCc.js} +1 -1
- package/web-app/dist/assets/{globe-C7TzpMng.js → globe-4-KwsIUo.js} +1 -1
- package/web-app/dist/assets/{hammer-B61u7zMJ.js → hammer-CaG_crBz.js} +1 -1
- package/web-app/dist/assets/{index-cu9_aTCP.js → index-DD7FC7yd.js} +3 -3
- package/web-app/dist/assets/{layers-D6JO2rbt.js → layers-BDQEO970.js} +1 -1
- package/web-app/dist/assets/{loader-circle-CUROrUhG.js → loader-circle-ENh48A-3.js} +1 -1
- package/web-app/dist/assets/{lock-BEhTudft.js → lock-CcI4ltMK.js} +1 -1
- package/web-app/dist/assets/{package-BkeBeiGM.js → package-DA_Bt7Mx.js} +1 -1
- package/web-app/dist/assets/{plus-Bv-Q1BYs.js → plus-r_jqdmwV.js} +1 -1
- package/web-app/dist/assets/{refresh-cw-CeT9llPq.js → refresh-cw-B0o3B073.js} +1 -1
- package/web-app/dist/assets/{rotate-ccw-aUzPGF5J.js → rotate-ccw-D5EyaAC-.js} +1 -1
- package/web-app/dist/assets/{scroll-text-DibSs5wF.js → scroll-text-DKrZwpKk.js} +1 -1
- package/web-app/dist/assets/{server-DEH19wNA.js → server-FMLinzaf.js} +1 -1
- package/web-app/dist/assets/{shield-alert-CtW0_p0i.js → shield-alert-C5YWhIfX.js} +1 -1
- package/web-app/dist/assets/{thumbs-up-BMp9hCP5.js → thumbs-up-lWteH19H.js} +1 -1
- package/web-app/dist/assets/{trash-2-s4Vf7Z8i.js → trash-2-CTWsJujI.js} +1 -1
- package/web-app/dist/assets/{trending-down-DnMzvTbm.js → trending-down-n969VpQb.js} +1 -1
- package/web-app/dist/assets/{trending-up-DnB-6eIb.js → trending-up-hNpwNjdL.js} +1 -1
- package/web-app/dist/assets/{upload-86CGmwiu.js → upload-DmcM8inI.js} +1 -1
- package/web-app/dist/assets/{usePolling-BaPZfGcd.js → usePolling-CQCRI-UO.js} +1 -1
- package/web-app/dist/assets/{user-CLepiTkS.js → user-BCspLAnr.js} +1 -1
- package/web-app/dist/index.html +1 -1
package/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: loki-mode
|
|
|
3
3
|
description: An autonomous software factory that knows what it is supposed to deliver, and proves it did. Use when the user says Loki Mode or asks to build, fix or verify software autonomously.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Loki Mode v11.17.
|
|
6
|
+
# Loki Mode v11.17.45
|
|
7
7
|
|
|
8
8
|
**Current entry points (Loki 10 engine):** `loki "<task>"`, `loki owner/repo#N` and `loki quick "<task>"` run the Loki 10 engine and end with a signed receipt (`loki verify`). `loki backlog owner/repo --all` runs many issues in parallel. Guide: `docs/v10/GUIDE.md`. The skill instructions below, and `loki start`, describe the previous bash loop. PRD-file starts (`loki start ./prd.md`) and the opencode provider still run on it until FC38-SWEEP ports them; the legacy-engine command and the LOKI_ENGINE switch remain removed. Prefer `loki owner/repo#N` for issues.
|
|
9
9
|
|
|
@@ -483,4 +483,4 @@ See `CHANGELOG.md` entries [7.5.7], [7.5.8], [7.5.13] for the per-fix list and r
|
|
|
483
483
|
|
|
484
484
|
---
|
|
485
485
|
|
|
486
|
-
**v11.17.
|
|
486
|
+
**v11.17.45 | [Autonomi](https://www.autonomi.dev/) flagship product | core skill**
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
11.17.
|
|
1
|
+
11.17.45
|
|
@@ -18,7 +18,7 @@ The scanner is honest by construction:
|
|
|
18
18
|
.git, vendor, bundled dist of dependencies) and oversized files, so it is
|
|
19
19
|
cheap and deterministic.
|
|
20
20
|
|
|
21
|
-
The
|
|
21
|
+
The v1 rules (rules 6 and 7 added later) (see internal/LOOP4-SECURE-BY-DEFAULT-PLAN.md):
|
|
22
22
|
1. private-key-committed (HIGH) a PEM private-key block in any text file
|
|
23
23
|
2. secret-in-client-file (HIGH) a real secret literal in a browser-served file
|
|
24
24
|
3. world-open-datastore (HIGH) a literal world-open datastore rule
|
|
@@ -46,7 +46,7 @@ import os
|
|
|
46
46
|
import re
|
|
47
47
|
import sys
|
|
48
48
|
|
|
49
|
-
RULES_VERSION = "1.
|
|
49
|
+
RULES_VERSION = "1.2"
|
|
50
50
|
|
|
51
51
|
# --- scan limits -----------------------------------------------------------
|
|
52
52
|
# Cap per-file size so a giant generated bundle cannot stall the gate. 2 MiB is
|
|
@@ -408,6 +408,48 @@ def _tls_rule_applies(rel_path):
|
|
|
408
408
|
return not norm.lower().endswith((".md", ".mdx", ".rst", ".txt", ".adoc"))
|
|
409
409
|
|
|
410
410
|
|
|
411
|
+
# ---------------------------------------------------------------------------
|
|
412
|
+
# rule 7: shell injection shapes
|
|
413
|
+
# ---------------------------------------------------------------------------
|
|
414
|
+
# Literal forms only, one line: subprocess.* with shell=True whose first
|
|
415
|
+
# argument is an f-string, a %-format, a .format() call or a concatenation;
|
|
416
|
+
# os.system() with an f-string, %-format, .format() or concatenation; and
|
|
417
|
+
# child_process exec/execSync with a template literal containing ${...}.
|
|
418
|
+
# Constant-string commands and list-form argv are never flagged.
|
|
419
|
+
_PY_DYNAMIC_ARG = (
|
|
420
|
+
r"""(?:[rRbB]?[fF][rR]?(?:"[^"]*"|'[^']*')"""
|
|
421
|
+
r"""|(?:"[^"]*"|'[^']*')\s*(?:%|\+|\.format\()"""
|
|
422
|
+
r"""|[A-Za-z_][\w.]*\s*\+\s*(?:"|'|[A-Za-z_]))"""
|
|
423
|
+
)
|
|
424
|
+
_SHELL_TRUE_SUBPROCESS = re.compile(
|
|
425
|
+
r"""\bsubprocess\.(?:run|call|Popen|check_output|check_call)\(\s*"""
|
|
426
|
+
+ _PY_DYNAMIC_ARG + r""".*\bshell\s*=\s*True\b"""
|
|
427
|
+
)
|
|
428
|
+
_OS_SYSTEM_DYNAMIC = re.compile(
|
|
429
|
+
r"""\bos\.(?:system|popen)\(\s*""" + _PY_DYNAMIC_ARG
|
|
430
|
+
)
|
|
431
|
+
_NODE_EXEC_TEMPLATE = re.compile(
|
|
432
|
+
r"""\b(?:child_process\.|cp\.)?(?:exec|execSync)\(\s*`[^`]*\$\{"""
|
|
433
|
+
)
|
|
434
|
+
_NODE_CP_FILE = re.compile(r"""\bchild_process\b""")
|
|
435
|
+
_SHELL_INJECTION_PATH_RE = _TLS_FIXTURE_PATH_RE
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def _shell_rule_applies(rel_path):
|
|
439
|
+
"""Same exclusions as rule 6: dev/test/fixture paths and prose docs."""
|
|
440
|
+
return _tls_rule_applies(rel_path)
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _shell_injection_hit(text, node_file):
|
|
444
|
+
if _SHELL_TRUE_SUBPROCESS.search(text) or _OS_SYSTEM_DYNAMIC.search(text):
|
|
445
|
+
return True
|
|
446
|
+
m = _NODE_EXEC_TEMPLATE.search(text)
|
|
447
|
+
if not m:
|
|
448
|
+
return False
|
|
449
|
+
qualified = text[max(0, m.start()):m.end()].lstrip().startswith(("child_process.", "cp."))
|
|
450
|
+
return qualified or node_file
|
|
451
|
+
|
|
452
|
+
|
|
411
453
|
# ---------------------------------------------------------------------------
|
|
412
454
|
# file walking / reading
|
|
413
455
|
# ---------------------------------------------------------------------------
|
|
@@ -471,6 +513,8 @@ def _scan_file(rel_path, lines, secrets_everywhere=False):
|
|
|
471
513
|
browser_served = _is_browser_served(rel_path)
|
|
472
514
|
prod_config = _is_production_config(rel_path)
|
|
473
515
|
tls_scan = _tls_rule_applies(rel_path)
|
|
516
|
+
shell_scan = _shell_rule_applies(rel_path)
|
|
517
|
+
node_file = shell_scan and any(_NODE_CP_FILE.search(t) for _, t in lines)
|
|
474
518
|
|
|
475
519
|
# rule 3 / rule 5 need whole-file context for the AND conditions.
|
|
476
520
|
saw_rls_disable = None # lineno of a DISABLE ROW LEVEL SECURITY
|
|
@@ -603,6 +647,18 @@ def _scan_file(rel_path, lines, secrets_everywhere=False):
|
|
|
603
647
|
"instead of turning verification off.",
|
|
604
648
|
))
|
|
605
649
|
|
|
650
|
+
# --- rule 7: shell injection shapes ------------------------------
|
|
651
|
+
if (shell_scan and not _COMMENT_OR_DOC_LINE.match(text)
|
|
652
|
+
and _shell_injection_hit(text, node_file)):
|
|
653
|
+
findings.append(_finding(
|
|
654
|
+
"shell-injection", rel_path, lineno, "HIGH",
|
|
655
|
+
"A shell command is built from a formatted or concatenated "
|
|
656
|
+
"string; any untrusted value in it becomes shell syntax.",
|
|
657
|
+
"Pass an argv list without shell=True (subprocess.run([cmd, "
|
|
658
|
+
"arg])) or use execFile/spawn with an args array; never "
|
|
659
|
+
"interpolate input into a command string.",
|
|
660
|
+
))
|
|
661
|
+
|
|
606
662
|
# --- rule 5: CORS wildcard + credentials (whole-file AND) --------
|
|
607
663
|
# Skip prose/doc lines (_rule3_skip = comment-only line or .md/.rst/.txt
|
|
608
664
|
# doc file): a README or comment WARNING about ACAO * + credentials is not
|
package/autonomy/loki
CHANGED
|
@@ -1757,14 +1757,15 @@ _help_answer() {
|
|
|
1757
1757
|
_help_verify_pr() {
|
|
1758
1758
|
echo -e "${BOLD}loki verify-pr${NC} - Verify a PR against its linked issue (experimental, LOKI_VERIFY_PR=1)"
|
|
1759
1759
|
echo ""
|
|
1760
|
-
echo "Usage: loki verify-pr <https://github.com/o/r/pull/N | o/r#N> [--out DIR]"
|
|
1760
|
+
echo "Usage: loki verify-pr <https://github.com/o/r/pull/N | o/r#N> [--out DIR] [--json]"
|
|
1761
1761
|
echo " loki verify-pr respond <o/r#N> fix open review comments (one commit each, on the PR"
|
|
1762
1762
|
echo " branch), push, reply with the sha; never resolves threads"
|
|
1763
1763
|
echo ""
|
|
1764
1764
|
echo "Derives a check from the PR's linked issue (Check: \`cmd\` or a loki-check fence)."
|
|
1765
1765
|
echo "VERIFIED only if the check fails on the PR base, passes on its head, and the"
|
|
1766
1766
|
echo "touched packages' suites pass. A PR with no linked issue, no check, a removed"
|
|
1767
|
-
echo "test or a non-discriminating check is NOT PROVEN. Writes verify-pr-result.json
|
|
1767
|
+
echo "test or a non-discriminating check is NOT PROVEN. Writes verify-pr-result.json;"
|
|
1768
|
+
echo "--json also prints that verdict object on stdout instead of the text summary."
|
|
1768
1769
|
echo "PR code runs only in the container sandbox (scripts/verify-pr-sandbox.sh); set"
|
|
1769
1770
|
echo "LOKI_VPR_IMAGE to a locally present image."
|
|
1770
1771
|
echo ""
|
|
@@ -1874,9 +1875,9 @@ show_help_all() {
|
|
|
1874
1875
|
echo ""
|
|
1875
1876
|
echo "Config:"
|
|
1876
1877
|
echo " config [cmd] Manage configuration (show|init|edit|path|set|get)"
|
|
1877
|
-
echo " doctor [--json] Check system prerequisites and skill symlinks"
|
|
1878
|
+
echo " doctor [--json] [--quiet] Check system prerequisites and skill symlinks"
|
|
1878
1879
|
echo ""
|
|
1879
|
-
echo " version
|
|
1880
|
+
echo " version [--json] Show version (--json for machine-readable)"
|
|
1880
1881
|
echo " help Show this help ('loki help aliases' for old names)"
|
|
1881
1882
|
echo ""
|
|
1882
1883
|
echo "More commands (the most useful; many more are dispatchable):"
|
|
@@ -7496,6 +7497,9 @@ cmd_provider() {
|
|
|
7496
7497
|
cmd_provider_set "$@"
|
|
7497
7498
|
;;
|
|
7498
7499
|
list)
|
|
7500
|
+
case " $* " in
|
|
7501
|
+
*" --json "*) _loki_bun_delegate provider list "$@" || { echo "loki provider list --json needs bun: ${_LOKI_DELEGATE_WHY}" >&2; return 1; } ;;
|
|
7502
|
+
esac
|
|
7499
7503
|
cmd_provider_list
|
|
7500
7504
|
;;
|
|
7501
7505
|
info)
|
|
@@ -7518,7 +7522,7 @@ cmd_provider() {
|
|
|
7518
7522
|
echo "Commands:"
|
|
7519
7523
|
echo " show Show current provider (default)"
|
|
7520
7524
|
echo " set Set provider for this project"
|
|
7521
|
-
echo " list List available providers"
|
|
7525
|
+
echo " list List available providers (--json: name, tier, installed, version)"
|
|
7522
7526
|
echo " info Show provider details (--json: capability contract)"
|
|
7523
7527
|
echo " matrix Show the capability matrix per provider and engine"
|
|
7524
7528
|
echo " models Show resolved model configuration for all providers"
|
|
@@ -10052,7 +10056,7 @@ _deploy_latest_receipt() {
|
|
|
10052
10056
|
return 0
|
|
10053
10057
|
}
|
|
10054
10058
|
|
|
10055
|
-
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha> [state] [health_status] [health_url] [rb_attempted] [rb_exit] [receipt_sha256] [started_at] [finished_at] [target] [canary] [post_rollback_health] [health_http_code] [health_match] [tree_sha] [destination] [deployed_url] [health_url_source] [env_keys_csv] [env_source] [domain] [domain_status] [tls_status] [tls_not_after] [domain_provenance]
|
|
10059
|
+
# _deploy_record <dir> <command> <exit_code> <run_id> <anchor_sha> [state] [health_status] [health_url] [rb_attempted] [rb_exit] [receipt_sha256] [started_at] [finished_at] [target] [canary] [post_rollback_health] [health_http_code] [health_match] [tree_sha] [destination] [deployed_url] [health_url_source] [env_keys_csv] [env_source] [domain] [domain_status] [tls_status] [tls_not_after] [domain_provenance] [live_binding]
|
|
10056
10060
|
# Writes the executed deploy's own record, then signs it (loki.deploy/1, Ed25519 via the
|
|
10057
10061
|
# receipt signer in loki-ts/src/util/receipt_signer.ts) so it is bound to the receipt that
|
|
10058
10062
|
# authorized it; `loki verify <record>` re-checks it offline. An unsigned record is reported
|
|
@@ -10082,6 +10086,7 @@ _deploy_record() {
|
|
|
10082
10086
|
local env_keys_csv="${23:-}" env_source="${24:-}"
|
|
10083
10087
|
# --domain: tls_status is "unchecked" unless the openssl proof ran and said otherwise.
|
|
10084
10088
|
local domain_rec="${25:-}" domain_status="${26:-}" tls_status="${27:-unchecked}" tls_not_after="${28:-}" domain_prov="${29:-}"
|
|
10089
|
+
local live_binding="${30:-}"
|
|
10085
10090
|
local ddir="$dir/.loki/deploys"
|
|
10086
10091
|
[ -d "${LOKI_DIR:-}" ] && ddir="${LOKI_DIR}/deploys"
|
|
10087
10092
|
mkdir -p "$ddir" 2>/dev/null || return 0
|
|
@@ -10091,7 +10096,7 @@ _deploy_record() {
|
|
|
10091
10096
|
DR_RBA="$rb_attempted" DR_RBX="$rb_exit" DR_RSHA="$receipt_sha" DR_T0="$started_at" \
|
|
10092
10097
|
DR_T1="$finished_at" DR_TGT="$target" DR_CAN="$canary" DR_PRB="$post_rb" DR_HC="$hcode_rec" DR_HM="$hmatch" DR_TREE="$tree_sha" DR_DEST="$dest_rec" DR_DURL="$deployed_url" DR_HSRC="$hsrc" \
|
|
10093
10098
|
DR_ENVK="$env_keys_csv" DR_ENVS="$env_source" \
|
|
10094
|
-
DR_DOM="$domain_rec" DR_DOMS="$domain_status" DR_TLS="$tls_status" DR_TLSA="$tls_not_after" DR_DOMP="$domain_prov" \
|
|
10099
|
+
DR_DOM="$domain_rec" DR_DOMS="$domain_status" DR_TLS="$tls_status" DR_TLSA="$tls_not_after" DR_DOMP="$domain_prov" DR_LB="$live_binding" \
|
|
10095
10100
|
python3 -c '
|
|
10096
10101
|
import json, os, datetime
|
|
10097
10102
|
state = os.environ.get("DR_STATE") or "anchored"
|
|
@@ -10130,6 +10135,7 @@ json.dump({
|
|
|
10130
10135
|
"tls_status": os.environ.get("DR_TLS") or "unchecked",
|
|
10131
10136
|
"tls_not_after": os.environ.get("DR_TLSA", ""),
|
|
10132
10137
|
"domain_provenance": os.environ.get("DR_DOMP", ""),
|
|
10138
|
+
"live_binding": os.environ.get("DR_LB") or "unchecked",
|
|
10133
10139
|
"health_http_code": int(os.environ["DR_HC"]) if os.environ.get("DR_HC", "").isdigit() else None,
|
|
10134
10140
|
}, open(os.environ["DR_OUT"], "w"), indent=2)
|
|
10135
10141
|
' 2>/dev/null || return 0
|
|
@@ -10228,6 +10234,91 @@ _deploy_probe_health() {
|
|
|
10228
10234
|
return 1
|
|
10229
10235
|
}
|
|
10230
10236
|
|
|
10237
|
+
# _deploy_provenance_root <dir> <type>
|
|
10238
|
+
# Echoes the static root that ships with the deploy and may carry /.well-known/, or "" when no
|
|
10239
|
+
# such root exists. nextjs and Vite: public/. Built static: the detected dist/ then build/.
|
|
10240
|
+
# docker, python and node serve no static root, so they print "" and are never bound.
|
|
10241
|
+
_deploy_provenance_root() {
|
|
10242
|
+
local dir="${1:-.}" type="${2:-}"
|
|
10243
|
+
case "$type" in
|
|
10244
|
+
nextjs) printf '%s' "$dir/public" ;;
|
|
10245
|
+
static)
|
|
10246
|
+
if [ -f "$dir/dist/index.html" ]; then printf '%s' "$dir/dist"
|
|
10247
|
+
elif [ -f "$dir/build/index.html" ]; then printf '%s' "$dir/build"
|
|
10248
|
+
elif [ -f "$dir/package.json" ] && grep -q '"vite"' "$dir/package.json" 2>/dev/null; then printf '%s' "$dir/public"
|
|
10249
|
+
fi
|
|
10250
|
+
;;
|
|
10251
|
+
esac
|
|
10252
|
+
return 0
|
|
10253
|
+
}
|
|
10254
|
+
|
|
10255
|
+
# _deploy_clear_provenance <dir>
|
|
10256
|
+
# Removes .well-known/loki-provenance.json from EVERY candidate static root (public/, dist/,
|
|
10257
|
+
# build/). Runs before each deploy decides whether to write a fresh doc, so a doc left by an
|
|
10258
|
+
# earlier deploy can never ship with a build it does not describe (unanchored override,
|
|
10259
|
+
# unsupported type, signing failure, or a root that moved between deploys).
|
|
10260
|
+
_deploy_clear_provenance() {
|
|
10261
|
+
local dir="${1:-.}" r
|
|
10262
|
+
for r in public dist build; do
|
|
10263
|
+
rm -f "$dir/$r/.well-known/loki-provenance.json" 2>/dev/null || true
|
|
10264
|
+
done
|
|
10265
|
+
return 0
|
|
10266
|
+
}
|
|
10267
|
+
|
|
10268
|
+
# _deploy_write_provenance <dir> <root> <head_sha> <tree_sha> <receipt_sha256> <run_id>
|
|
10269
|
+
# Writes <root>/.well-known/loki-provenance.json and signs it in place (loki verify
|
|
10270
|
+
# --sign-provenance). Called only after the gate (including the dirty-tree check) passed, so the
|
|
10271
|
+
# untracked file never dirties the tree that was verified; it is also added to .git/info/exclude
|
|
10272
|
+
# so the NEXT deploy's dirty-tree gate does not trip over it. Returns non-zero when not signed.
|
|
10273
|
+
_deploy_write_provenance() {
|
|
10274
|
+
local dir="$1" root="$2" head="$3" tree="$4" rsha="$5" rid="$6"
|
|
10275
|
+
local f="$root/.well-known/loki-provenance.json" ex
|
|
10276
|
+
rm -f "$f" 2>/dev/null
|
|
10277
|
+
[ -n "$head" ] && [ -n "$tree" ] && [ -n "$rsha" ] && [ -n "$rid" ] || return 1
|
|
10278
|
+
mkdir -p "$root/.well-known" 2>/dev/null || return 1
|
|
10279
|
+
PV_H="$head" PV_T="$tree" PV_R="$rsha" PV_I="$rid" PV_OUT="$f" python3 -c '
|
|
10280
|
+
import json, os, datetime
|
|
10281
|
+
json.dump({"schema": "loki.provenance/1", "head_sha": os.environ["PV_H"], "tree_sha": os.environ["PV_T"],
|
|
10282
|
+
"receipt_sha256": os.environ["PV_R"], "run_id": os.environ["PV_I"],
|
|
10283
|
+
"built_at": datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")},
|
|
10284
|
+
open(os.environ["PV_OUT"], "w"), indent=2)
|
|
10285
|
+
' 2>/dev/null || { rm -f "$f"; return 1; }
|
|
10286
|
+
ex="$(git -C "$dir" rev-parse --git-path info/exclude 2>/dev/null)" || ex=""
|
|
10287
|
+
if [ -n "$ex" ]; then
|
|
10288
|
+
case "$ex" in /*) ;; *) ex="$dir/$ex" ;; esac
|
|
10289
|
+
grep -qxF '**/.well-known/loki-provenance.json' "$ex" 2>/dev/null \
|
|
10290
|
+
|| { mkdir -p "$(dirname "$ex")" 2>/dev/null; printf '%s\n' '**/.well-known/loki-provenance.json' >> "$ex" 2>/dev/null; } || true
|
|
10291
|
+
fi
|
|
10292
|
+
if ! ( cd "$dir" && _loki_bun_delegate engine10 verify --sign-provenance "$f" ) >/dev/null 2>&1; then
|
|
10293
|
+
rm -f "$f"
|
|
10294
|
+
return 1
|
|
10295
|
+
fi
|
|
10296
|
+
return 0
|
|
10297
|
+
}
|
|
10298
|
+
|
|
10299
|
+
# _deploy_probe_live_binding <dir> <url> <anchor>
|
|
10300
|
+
# Fetches <url>/.well-known/loki-provenance.json through loki verify --url --expect-sha.
|
|
10301
|
+
# Returns 0 VERIFIED (signature holds and head_sha equals the anchor), 1 MISMATCH (a doc
|
|
10302
|
+
# was served but is forged or names a different SHA, still so on the last bounded retry),
|
|
10303
|
+
# 2 UNCHECKED (no doc could be fetched or its key is unknown, after a bounded retry with the
|
|
10304
|
+
# health-probe tunables). Never VERIFIED on 2.
|
|
10305
|
+
_deploy_probe_live_binding() {
|
|
10306
|
+
local dir="$1" url="$2" anchor="$3"
|
|
10307
|
+
local attempts="${LOKI_DEPLOY_HEALTH_ATTEMPTS:-5}" interval="${LOKI_DEPLOY_HEALTH_INTERVAL:-3}" i=0 vrc=2
|
|
10308
|
+
case "$attempts" in ''|*[!0-9]*) attempts=5 ;; esac
|
|
10309
|
+
case "$interval" in ''|*[!0-9]*) interval=3 ;; esac
|
|
10310
|
+
while [ "$i" -lt "$attempts" ]; do
|
|
10311
|
+
i=$((i + 1))
|
|
10312
|
+
vrc=0
|
|
10313
|
+
( cd "$dir" && _loki_bun_delegate engine10 verify --url "$url" --expect-sha "$anchor" ) >/dev/null 2>&1 || vrc=$?
|
|
10314
|
+
[ "$vrc" -eq 0 ] && return 0
|
|
10315
|
+
[ "$i" -lt "$attempts" ] && [ "$interval" -gt 0 ] && sleep "$interval"
|
|
10316
|
+
done
|
|
10317
|
+
# A mismatch on the last attempt is FAILED; earlier mismatches may be propagation of the previous build.
|
|
10318
|
+
[ "$vrc" -eq 1 ] && return 1
|
|
10319
|
+
return 2
|
|
10320
|
+
}
|
|
10321
|
+
|
|
10231
10322
|
# _deploy_policy_refusal <dir>
|
|
10232
10323
|
# Echoes a refusal reason when the policy file does not permit a deploy, else "".
|
|
10233
10324
|
# No policy file: "" (unchanged behaviour). File present and node or check.js
|
|
@@ -10607,6 +10698,25 @@ _deploy_try_execute() {
|
|
|
10607
10698
|
esac
|
|
10608
10699
|
fi
|
|
10609
10700
|
|
|
10701
|
+
# Provenance doc: shipped with the build so the live URL can prove which SHA it serves.
|
|
10702
|
+
local live_binding="unchecked" prov_root="" prov_written=false
|
|
10703
|
+
prov_root="$(_deploy_provenance_root "$dir" "$type")"
|
|
10704
|
+
# Every branch below starts from no doc: only a doc written by THIS deploy may ship.
|
|
10705
|
+
_deploy_clear_provenance "$dir"
|
|
10706
|
+
if [ "$anchor_state" = "unanchored_overridden" ]; then
|
|
10707
|
+
live_binding="unanchored"
|
|
10708
|
+
echo "Live binding unanchored: the evidence gate was overridden with the anchor UNPROVEN, so no provenance doc is published and the live URL is not bound to ${anchor:0:12}." >&2
|
|
10709
|
+
elif [ -z "$prov_root" ]; then
|
|
10710
|
+
live_binding="unsupported"
|
|
10711
|
+
echo "Live binding unsupported: a ${type} project has no static root to serve /.well-known/loki-provenance.json, so the live URL is not bound to ${anchor:0:12}." >&2
|
|
10712
|
+
elif _deploy_write_provenance "$dir" "$prov_root" "$anchor" "$(git -C "$dir" rev-parse 'HEAD^{tree}' 2>/dev/null || true)" \
|
|
10713
|
+
"$(python3 -c 'import json,sys;print((json.load(open(sys.argv[1])).get("verification") or {}).get("hash") or "")' "$pj" 2>/dev/null || true)" "$run_id"; then
|
|
10714
|
+
prov_written=true
|
|
10715
|
+
echo "Provenance written: ${prov_root}/.well-known/loki-provenance.json"
|
|
10716
|
+
else
|
|
10717
|
+
echo -e "${YELLOW}WARNING: the provenance doc could not be signed, so the live URL will not be bound to this SHA.${NC}" >&2
|
|
10718
|
+
fi
|
|
10719
|
+
|
|
10610
10720
|
local rc=0 started_at finished_at purl="" canary_out=""
|
|
10611
10721
|
local -a deploy_argv=()
|
|
10612
10722
|
read -r -a deploy_argv <<<"$exec_cmd"
|
|
@@ -10691,8 +10801,31 @@ _deploy_try_execute() {
|
|
|
10691
10801
|
fi
|
|
10692
10802
|
fi
|
|
10693
10803
|
|
|
10694
|
-
|
|
10804
|
+
# Live binding: the deployed URL must serve the provenance doc for THIS anchor.
|
|
10805
|
+
if [ "$prov_written" = true ] && [ "$rc" -eq 0 ] && [ "$health_status" != "FAILED_HEALTH" ] && [ -n "$deployed_url" ]; then
|
|
10806
|
+
echo "Probing live binding: ${deployed_url%/}/.well-known/loki-provenance.json"
|
|
10807
|
+
local lb_rc=0
|
|
10808
|
+
_deploy_probe_live_binding "$dir" "$deployed_url" "$anchor" || lb_rc=$?
|
|
10809
|
+
case "$lb_rc" in
|
|
10810
|
+
0)
|
|
10811
|
+
live_binding="VERIFIED"
|
|
10812
|
+
echo -e "${GREEN}Live binding VERIFIED:${NC} ${deployed_url} serves a signed provenance doc for ${anchor:0:12}."
|
|
10813
|
+
;;
|
|
10814
|
+
1)
|
|
10815
|
+
live_binding="FAILED"
|
|
10816
|
+
health_status="FAILED_HEALTH"
|
|
10817
|
+
echo -e "${RED}FAILED_HEALTH: ${deployed_url} served a provenance doc that is forged or is not for ${anchor:0:12} (stale or wrong build served).${NC}" >&2
|
|
10818
|
+
;;
|
|
10819
|
+
*)
|
|
10820
|
+
echo " Live binding unchecked: no verifiable provenance doc could be fetched from ${deployed_url}. Not a claim the live build is ${anchor:0:12}." >&2
|
|
10821
|
+
;;
|
|
10822
|
+
esac
|
|
10823
|
+
fi
|
|
10824
|
+
|
|
10825
|
+
if [ "$health_status" = "FAILED_HEALTH" ] && [ "$live_binding" != "FAILED" ]; then
|
|
10695
10826
|
echo -e "${RED}FAILED_HEALTH: ${health_url} did not return 2xx${health_match:+ containing \"${health_match}\"} (last status ${hcode:-000}).${NC}" >&2
|
|
10827
|
+
fi
|
|
10828
|
+
if [ "$health_status" = "FAILED_HEALTH" ]; then
|
|
10696
10829
|
if [ "$auto_rollback" = true ] && [ -n "$rb_cmd" ] && ! _deploy_is_destructive "$rb_cmd"; then
|
|
10697
10830
|
echo "Rolling back: ${rb_cmd}" >&2
|
|
10698
10831
|
rb_attempted=true
|
|
@@ -10731,7 +10864,7 @@ _deploy_try_execute() {
|
|
|
10731
10864
|
rec="$(_deploy_record "$dir" "$exec_disp" "$rc" "${run_id:-unknown}" "${anchor:-unknown}" \
|
|
10732
10865
|
"$anchor_state" "$health_status" "$health_url" "$rb_attempted" "$rb_exit" \
|
|
10733
10866
|
"$rsha" "$started_at" "$finished_at" "${command%% *}" "$canary" "$post_rb" "$hcode" "$health_match" "$(git -C "$dir" rev-parse 'HEAD^{tree}' 2>/dev/null || true)" "$dest" "$deployed_url" "$health_src" "$env_keys" "$env_file" \
|
|
10734
|
-
"$domain" "$_DD_STATUS" "$_DD_TLS" "$_DD_NOTAFTER" "$_DD_PROV")"
|
|
10867
|
+
"$domain" "$_DD_STATUS" "$_DD_TLS" "$_DD_NOTAFTER" "$_DD_PROV" "$live_binding")"
|
|
10735
10868
|
echo ""
|
|
10736
10869
|
if [ "$rc" -eq 0 ]; then
|
|
10737
10870
|
echo -e "${GREEN}Deploy command exited 0.${NC}"
|
|
@@ -15488,10 +15621,19 @@ cmd_version() {
|
|
|
15488
15621
|
--help|-h|help)
|
|
15489
15622
|
echo -e "${BOLD}Loki Mode -- print version${NC}"
|
|
15490
15623
|
echo ""
|
|
15491
|
-
echo "Usage: loki version"
|
|
15624
|
+
echo "Usage: loki version [--json]"
|
|
15492
15625
|
echo ""
|
|
15493
15626
|
echo "Prints 'Loki Mode vX.Y.Z' from the VERSION file shipped with"
|
|
15494
15627
|
echo "the package. Equivalent to 'loki --version' and 'loki -v'."
|
|
15628
|
+
echo ""
|
|
15629
|
+
echo " --json Print {\"version\",\"runtime\",\"install_path\"} as one JSON line"
|
|
15630
|
+
return 0
|
|
15631
|
+
;;
|
|
15632
|
+
--json)
|
|
15633
|
+
local _vp="${SKILL_DIR:-}"
|
|
15634
|
+
_vp="${_vp//\\/\\\\}"
|
|
15635
|
+
_vp="${_vp//\"/\\\"}"
|
|
15636
|
+
printf '{"version":"%s","runtime":"bash","install_path":"%s"}\n' "$(get_version)" "$_vp"
|
|
15495
15637
|
return 0
|
|
15496
15638
|
;;
|
|
15497
15639
|
esac
|
|
@@ -25722,7 +25864,7 @@ except Exception as e:
|
|
|
25722
25864
|
echo "Usage: loki memory <command> [args]"
|
|
25723
25865
|
echo ""
|
|
25724
25866
|
echo "Learnings Commands:"
|
|
25725
|
-
echo " list
|
|
25867
|
+
echo " list [--json] Show summary of all learnings"
|
|
25726
25868
|
echo " show [type] Show entries (patterns|mistakes|successes|all)"
|
|
25727
25869
|
echo " Options: --limit N, --project P"
|
|
25728
25870
|
echo " search <query> Search across all learnings"
|
package/completions/_loki
CHANGED
|
@@ -73,6 +73,7 @@ function _loki {
|
|
|
73
73
|
doctor)
|
|
74
74
|
_arguments \
|
|
75
75
|
'--json[Output machine-readable JSON]' \
|
|
76
|
+
'(-q --quiet)'{-q,--quiet}'[Print only failed and warned checks plus the summary]' \
|
|
76
77
|
'--fix[Create state dirs and a default config; offer claude install and login]' \
|
|
77
78
|
'--yes[With --fix: run the claude install and login without asking]' \
|
|
78
79
|
'--help[Show help]'
|
|
@@ -164,6 +165,9 @@ function _loki {
|
|
|
164
165
|
completions)
|
|
165
166
|
_arguments '1:shell:(bash zsh install)'
|
|
166
167
|
;;
|
|
168
|
+
version)
|
|
169
|
+
_arguments '--json[Print version, runtime and install path as JSON]' '--help[Show help]'
|
|
170
|
+
;;
|
|
167
171
|
monitor)
|
|
168
172
|
_directories
|
|
169
173
|
;;
|
|
@@ -186,6 +190,12 @@ function _loki {
|
|
|
186
190
|
'--auto-rollback[Roll back on a failed health probe]' \
|
|
187
191
|
'--help[Show help]'
|
|
188
192
|
;;
|
|
193
|
+
verify-pr)
|
|
194
|
+
_arguments \
|
|
195
|
+
'--out[Result directory]:dir:_files -/' \
|
|
196
|
+
'--json[Print the verdict object on stdout]' \
|
|
197
|
+
'--help[Show help]'
|
|
198
|
+
;;
|
|
189
199
|
verify)
|
|
190
200
|
_arguments \
|
|
191
201
|
'--pubkey[Check against this public key only]:key file:_files' \
|
|
@@ -345,7 +355,7 @@ function _loki_commands {
|
|
|
345
355
|
'bench:Run benchmarks'
|
|
346
356
|
'open:Open a project resource'
|
|
347
357
|
'rc:Release candidate commands'
|
|
348
|
-
'version:Show version'
|
|
358
|
+
'version:Show version (--json for machine-readable)'
|
|
349
359
|
'completions:Output shell completions'
|
|
350
360
|
'help:Show help'
|
|
351
361
|
)
|
|
@@ -435,7 +445,7 @@ function _loki_provider {
|
|
|
435
445
|
cmds=(
|
|
436
446
|
'show:Display current provider'
|
|
437
447
|
'set:Set default provider'
|
|
438
|
-
'list:List available providers'
|
|
448
|
+
'list:List available providers (--json: name, tier, installed, version)'
|
|
439
449
|
'info:Get provider information (--json: capability contract)'
|
|
440
450
|
'matrix:Show the capability matrix'
|
|
441
451
|
'help:Provider help'
|
package/completions/loki.bash
CHANGED
|
@@ -72,6 +72,10 @@ _loki_completion() {
|
|
|
72
72
|
;;
|
|
73
73
|
|
|
74
74
|
provider)
|
|
75
|
+
if [[ $cword -ge 3 && ( "${words[2]}" == "list" || "${words[2]}" == "info" ) ]]; then
|
|
76
|
+
COMPREPLY=( $(compgen -W "--json" -- "$cur") )
|
|
77
|
+
return 0
|
|
78
|
+
fi
|
|
75
79
|
local provider_cmds="show set list info matrix help"
|
|
76
80
|
COMPREPLY=( $(compgen -W "${provider_cmds}" -- "$cur") )
|
|
77
81
|
;;
|
|
@@ -143,7 +147,7 @@ _loki_completion() {
|
|
|
143
147
|
;;
|
|
144
148
|
doctor)
|
|
145
149
|
if [[ "$cur" == -* ]]; then
|
|
146
|
-
COMPREPLY=( $(compgen -W "--json --fix --yes --repo --requirements --help" -- "$cur") )
|
|
150
|
+
COMPREPLY=( $(compgen -W "--json --quiet --fix --yes --repo --requirements --help" -- "$cur") )
|
|
147
151
|
return 0
|
|
148
152
|
fi
|
|
149
153
|
;;
|
|
@@ -283,6 +287,13 @@ _loki_completion() {
|
|
|
283
287
|
return 0
|
|
284
288
|
;;
|
|
285
289
|
|
|
290
|
+
verify-pr)
|
|
291
|
+
if [[ "$cur" == -* ]]; then
|
|
292
|
+
COMPREPLY=( $(compgen -W "--out --json --help" -- "$cur") )
|
|
293
|
+
return 0
|
|
294
|
+
fi
|
|
295
|
+
;;
|
|
296
|
+
|
|
286
297
|
undo)
|
|
287
298
|
if [[ "$cur" == -* ]]; then
|
|
288
299
|
COMPREPLY=( $(compgen -W "--list --plan --dry-run --help" -- "$cur") )
|
|
@@ -335,6 +346,10 @@ _loki_completion() {
|
|
|
335
346
|
_filedir -d
|
|
336
347
|
;;
|
|
337
348
|
|
|
349
|
+
version)
|
|
350
|
+
COMPREPLY=( $(compgen -W "--json --help" -- "$cur") )
|
|
351
|
+
;;
|
|
352
|
+
|
|
338
353
|
completions)
|
|
339
354
|
COMPREPLY=( $(compgen -W "bash zsh install" -- "$cur") )
|
|
340
355
|
;;
|
package/dashboard/__init__.py
CHANGED
package/docs/CLI-REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI reference
|
|
2
2
|
|
|
3
|
-
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.17.
|
|
3
|
+
Generated from `loki-ts/src/cli/registry.ts` by `scripts/generate-stale-zero.sh` for v11.17.45. Do not edit by hand.
|
|
4
4
|
Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for the full inventory.
|
|
5
5
|
|
|
6
6
|
| Command | Description |
|
|
@@ -71,6 +71,8 @@ Hidden and legacy-only commands are omitted; see `docs/v10/CLI-MODERN.md` for th
|
|
|
71
71
|
| `loki queue` | Overnight issue queue with a morning digest |
|
|
72
72
|
| `loki queue add` | Queue issue refs |
|
|
73
73
|
| `loki queue list` | Show the queue |
|
|
74
|
+
| `loki queue remove` | Take issue refs out of the queue |
|
|
75
|
+
| `loki queue clear` | Empty the queue |
|
|
74
76
|
| `loki queue run` | Process the queue |
|
|
75
77
|
| `loki issues` | Run every open issue in a repo and open PRs |
|
|
76
78
|
| `loki issues run` | Triage and run open issues; split too-large ones into stacked PRs |
|
package/docs/INSTALLATION.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Loki Mode, by [Autonomi](https://www.autonomi.dev/), turns a spec or an issue into a verified pull request with a receipt you can check. This page covers installing it, a first run, and the options around it.
|
|
4
4
|
|
|
5
|
-
**Version:** v11.17.
|
|
5
|
+
**Version:** v11.17.45
|
|
6
6
|
|
|
7
7
|
**Engine note:** `loki start` is the primary command. `loki start`, `loki "<task>"`, `loki owner/repo#N` and `loki quick` all run the Loki 10 engine, whatever the flag order or input form (PRD file, issue ref, task or one-word brief). The previous loop runs only on `--engine legacy`, `LOKI_ENGINE=legacy`, or for one call when a flag Loki 10 cannot honor is present. See the [user guide](v10/GUIDE.md) and the [5-minute quickstart](QUICKSTART.md).
|
|
8
8
|
|
package/docs/WORKSPACES.md
CHANGED
|
@@ -55,7 +55,7 @@ After `loki workspace run <name> <ref>` finishes, check the evidence rather than
|
|
|
55
55
|
3. `.loki/workspaces/<name>/<run-id>/integration.json` holds `status`, the head SHA per repo and `timing`.
|
|
56
56
|
4. `python3 autonomy/lib/workspace_metrics.py <run-dir> --attention-min <minutes>` prints `prs`, `prs_per_hour` and `prs_per_attention_minute`; any value it cannot measure prints `unmeasured`.
|
|
57
57
|
5. Each repo PR for the run branch carries exactly one integration comment.
|
|
58
|
-
6. `loki verify <run-dir>/composite.json` re-checks the composite offline (add `--pubkey FILE` on another machine). It recomputes the composite hash, re-verifies every child receipt from disk and checks the signature. It prints the integration result and every repo head SHA it attests. It is TAMPERED for a tampered or swapped child, a child head that is not the integrated head, a failed or interrupted integration status or nonzero exit code, an edited integration section, or an `integration.log` that no longer matches its recorded hash. It is UNCHECKED for an unsigned child under a signed composite, and for a legacy composite that predates the integration section and has no integrated head to compare. Deployed-SHA binding is not
|
|
58
|
+
6. `loki verify <run-dir>/composite.json` re-checks the composite offline (add `--pubkey FILE` on another machine). It recomputes the composite hash, re-verifies every child receipt from disk and checks the signature. It prints the integration result and every repo head SHA it attests. It is TAMPERED for a tampered or swapped child, a child head that is not the integrated head, a failed or interrupted integration status or nonzero exit code, an edited integration section, or an `integration.log` that no longer matches its recorded hash. It is UNCHECKED for an unsigned child under a signed composite, and for a legacy composite that predates the integration section and has no integrated head to compare. Deployed-SHA binding is partial: `loki deploy --execute` ships a signed `.well-known/loki-provenance.json` (head_sha, tree_sha, receipt_sha256, run_id, built_at) in the static root of a single-repo nextjs, Vite or built-static project (public/, dist/ or build/) and, when the provider prints a URL, fetches it back through `loki verify --url --expect-sha`. The deploy record's `live_binding` is VERIFIED only when the served doc is signed and names the anchor SHA; a stale or forged doc is FAILED (FAILED_HEALTH, exit 4), an unreachable one is `unchecked`, and docker, python and node projects (no static root) are `unsupported`. A mismatch is retried inside the health attempts and interval budget before it is FAILED, so a URL still serving the previous build for a few seconds is not rolled back at once. A live doc that is unsigned or signed with an unknown key is `unchecked`, not FAILED, so a stale non-loki build serving such a doc passes silently. Under `--allow-unanchored` the anchor is UNPROVEN, so no doc is published and `live_binding` is `unanchored`. The deploy appends `**/.well-known/loki-provenance.json` to `.git/info/exclude` and leaves the file in the working tree. Every `loki deploy --execute` first removes that file from public/, dist/ and build/, so only a doc written by the current deploy ships; an unanchored, unsupported or unsigned deploy ships none. A provider run by hand outside `loki deploy --execute` uploads whatever doc the last loki deploy left, which names that deploy's SHA, not the new build. If the provider rebuilds remotely from a gitignored root, the doc is lost and the result is `unchecked`. None of this binds a composite: the composite attests what was integrated and tested, not what is running anywhere, and no workspace deploy writes or probes a provenance doc. Exit codes are the single-receipt ones: 0 VERIFIED, 1 TAMPERED, 2 UNCHECKED, 3 UNSIGNED. The composite never reads higher than its weakest child: one PARTIAL child caps it at PARTIAL.
|
|
59
59
|
7. A run that could not seal prints `composite not sealed: <reason>` (for example a repo with no receipt). Seal by hand with `loki verify --seal-composite <run-dir>`; it refuses unless every child has a receipt.
|
|
60
60
|
|
|
61
61
|
Not measured: no scale test exists for large repo counts. Claim only what you have run.
|