loki-mode 11.5.1 → 11.7.1

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.
Files changed (116) hide show
  1. package/README.md +19 -2
  2. package/SKILL.md +2 -2
  3. package/VERSION +1 -1
  4. package/autonomy/lib/backlog.py +11 -2
  5. package/autonomy/lib/chat.sh +57 -1
  6. package/autonomy/lib/loki_yaml.py +8 -2
  7. package/autonomy/lib/workspace.py +267 -11
  8. package/autonomy/loki +327 -222
  9. package/autonomy/notification-checker.py +1 -1
  10. package/autonomy/trigger-server.py +185 -2
  11. package/autonomy/verify.sh +4 -0
  12. package/completions/_loki +4 -0
  13. package/completions/loki.bash +20 -2
  14. package/dashboard/__init__.py +3 -10
  15. package/dashboard/control.py +5 -435
  16. package/docs/ARCHITECTURE.md +1 -2
  17. package/docs/CLI-CORE-VERBS.md +2 -6
  18. package/docs/CLI-REFERENCE.md +2 -1
  19. package/docs/COMPONENTS.md +0 -2
  20. package/docs/DEPLOY-PLAN.md +17 -8
  21. package/docs/EVALUATING.md +8 -0
  22. package/docs/INSTALLATION.md +1 -1
  23. package/docs/WORKSPACES.md +15 -2
  24. package/docs/authentication.md +19 -1
  25. package/docs/dev/architecture-reference.md +8 -0
  26. package/docs/dev/release-checklist.md +9 -8
  27. package/docs/enterprise/security.md +3 -234
  28. package/docs/sentry-intake.md +11 -0
  29. package/docs/v10/ARCHITECT-CUTS.md +304 -0
  30. package/docs/v10/CLI-MODERN.md +1 -1
  31. package/docs/v10/DECISIONS.md +5 -5
  32. package/docs/v10/ENGINE.md +735 -299
  33. package/docs/v10/GUARDS.md +16 -14
  34. package/docs/v10/GUIDE.md +5 -7
  35. package/docs/v10/PROGRESS.md +12 -1
  36. package/docs/v11/MARKET-SLICES.md +1 -5
  37. package/integrations/openclaw/README.md +20 -0
  38. package/integrations/vibe-kanban.md +12 -50
  39. package/loki-ts/dist/loki.js +1213 -1178
  40. package/mcp/__init__.py +1 -1
  41. package/mcp/_sdk_loader.py +1 -1
  42. package/package.json +2 -2
  43. package/packages/control-plane/dist/server.js +1090 -924
  44. package/plugins/loki-mode/.claude-plugin/plugin.json +1 -1
  45. package/references/quality-control.md +1 -116
  46. package/schemas/loki-yaml.schema.json +5 -2
  47. package/skills/00-index.md +2 -2
  48. package/skills/artifacts.md +0 -22
  49. package/skills/production.md +2 -38
  50. package/src/policies/engine.js +41 -2
  51. package/src/policies/types.js +49 -0
  52. package/web-app/dist/assets/{AdminPage-Cbu3pXL8.js → AdminPage-S3BYeapm.js} +1 -1
  53. package/web-app/dist/assets/{Avatar-DGqGGcGx.js → Avatar-C1rHwLgn.js} +1 -1
  54. package/web-app/dist/assets/{Badge-Bhlipt6v.js → Badge-CzlnN5aQ.js} +1 -1
  55. package/web-app/dist/assets/{Button-CpN2sO61.js → Button-B1w5AeGC.js} +1 -1
  56. package/web-app/dist/assets/{CockpitPage-BqeDJwdL.js → CockpitPage-Dyeqjfkc.js} +1 -1
  57. package/web-app/dist/assets/{ComparePage-DUieA2ba.js → ComparePage-C6y2GkGR.js} +1 -1
  58. package/web-app/dist/assets/{ErrorBoundary-DTWsYrOm.js → ErrorBoundary-Dg2fcGkR.js} +1 -1
  59. package/web-app/dist/assets/{EvidenceReceiptPanel-BU_B9X19.js → EvidenceReceiptPanel-CxbyjvTj.js} +1 -1
  60. package/web-app/dist/assets/{GitHubIssuesPanel-DEZoLOsP.js → GitHubIssuesPanel-Dpys1xDb.js} +1 -1
  61. package/web-app/dist/assets/{GitHubPRsPanel-BEsnrq7Y.js → GitHubPRsPanel-CRVjWoFl.js} +1 -1
  62. package/web-app/dist/assets/{HomePage-BoUVuq7h.js → HomePage-BKnv-oYw.js} +2 -2
  63. package/web-app/dist/assets/{LoginPage-CmOED7vb.js → LoginPage-BGksmFRd.js} +1 -1
  64. package/web-app/dist/assets/{MagicPage-DATZitrZ.js → MagicPage-OCvhp16M.js} +1 -1
  65. package/web-app/dist/assets/{MetricsPage-DGL1v0I-.js → MetricsPage-CJOJpXWC.js} +1 -1
  66. package/web-app/dist/assets/{NotFoundPage-C2WgOjZd.js → NotFoundPage-C04p3QvN.js} +1 -1
  67. package/web-app/dist/assets/{ProjectPage-BMi1tepj.js → ProjectPage-BRB8txsY.js} +3 -3
  68. package/web-app/dist/assets/{ProjectsPage-vc3y5YPh.js → ProjectsPage-CoUYeQEh.js} +1 -1
  69. package/web-app/dist/assets/{SettingsPage-kbq0Ftvi.js → SettingsPage-BkjmJGfM.js} +1 -1
  70. package/web-app/dist/assets/{ShowcasePage-Bn6Q8Kbg.js → ShowcasePage-B4vU9TjQ.js} +1 -1
  71. package/web-app/dist/assets/{SystemSettingsPage-Basmhs_b.js → SystemSettingsPage-Z2jiVsni.js} +1 -1
  72. package/web-app/dist/assets/{TeamsPage-BTqOc4TR.js → TeamsPage-Ulm8SISq.js} +1 -1
  73. package/web-app/dist/assets/{TemplatesPage-2P9002VL.js → TemplatesPage-C_7MM6Ey.js} +1 -1
  74. package/web-app/dist/assets/{TerminalOutput-CAoSPBrg.js → TerminalOutput-Cumd7Tgs.js} +1 -1
  75. package/web-app/dist/assets/{activity-BcWVWCqU.js → activity-CZAUkbiY.js} +1 -1
  76. package/web-app/dist/assets/{bell-CYRqqgZ6.js → bell-BOvSXpEr.js} +1 -1
  77. package/web-app/dist/assets/{bot-CU7gdn3g.js → bot-DOTZxBYj.js} +1 -1
  78. package/web-app/dist/assets/{check-DLlCpz9V.js → check-BxpE0B5u.js} +1 -1
  79. package/web-app/dist/assets/{chevron-left-CE9Cb30C.js → chevron-left-D6KPvBOA.js} +1 -1
  80. package/web-app/dist/assets/{circle-alert-uou4bjua.js → circle-alert-Bajq86oe.js} +1 -1
  81. package/web-app/dist/assets/{clock-C_dSJcDX.js → clock-CnlHAVJc.js} +1 -1
  82. package/web-app/dist/assets/{cloud-DZ22ITF6.js → cloud-DjaymjwM.js} +1 -1
  83. package/web-app/dist/assets/{code-xml-BrJS0hhS.js → code-xml-BADEw2Ld.js} +1 -1
  84. package/web-app/dist/assets/{database-D2HOmxtl.js → database-B4aKb5Cq.js} +1 -1
  85. package/web-app/dist/assets/{dollar-sign-CQ1vK_6q.js → dollar-sign-DrldXpja.js} +1 -1
  86. package/web-app/dist/assets/{file-code-corner-w2vpGm5d.js → file-code-corner-DVebDVR-.js} +1 -1
  87. package/web-app/dist/assets/{file-plus-DlF6wjkD.js → file-plus-BD-N_-Tl.js} +1 -1
  88. package/web-app/dist/assets/{globe-Dy3lBJg1.js → globe-C7ieInC-.js} +1 -1
  89. package/web-app/dist/assets/{hammer-C3xIiRXN.js → hammer-BSugKIji.js} +1 -1
  90. package/web-app/dist/assets/{index-4t-_IW2y.js → index-nLQgfvOn.js} +3 -3
  91. package/web-app/dist/assets/{layers-Y8Fs8qXI.js → layers-CHXxU2on.js} +1 -1
  92. package/web-app/dist/assets/{loader-circle-CxgN1v4m.js → loader-circle-BEEz0LbD.js} +1 -1
  93. package/web-app/dist/assets/{lock-DFFNbXDs.js → lock-CzQGfcnE.js} +1 -1
  94. package/web-app/dist/assets/{package-5e6qCTUg.js → package-CcHYQLGd.js} +1 -1
  95. package/web-app/dist/assets/{plus-C0Go_ytj.js → plus-DdzgFEXn.js} +1 -1
  96. package/web-app/dist/assets/{refresh-cw-D4i-NW3o.js → refresh-cw-7x0HbH9Y.js} +1 -1
  97. package/web-app/dist/assets/{rotate-ccw-YlKkiUW6.js → rotate-ccw-DBahX1Sp.js} +1 -1
  98. package/web-app/dist/assets/{scroll-text-4qXOHTap.js → scroll-text-BB8y8a9Z.js} +1 -1
  99. package/web-app/dist/assets/{server-BB6zXNp9.js → server-CfkbW2MH.js} +1 -1
  100. package/web-app/dist/assets/{shield-alert-D4tzjB1H.js → shield-alert-DUgD8rBF.js} +1 -1
  101. package/web-app/dist/assets/{thumbs-up-DZiRwdFj.js → thumbs-up-DGoyY6QC.js} +1 -1
  102. package/web-app/dist/assets/{trash-2-Dv_LkRIK.js → trash-2-B6xrSUMr.js} +1 -1
  103. package/web-app/dist/assets/{trending-down-BJYp9PQe.js → trending-down-B6RGKTOx.js} +1 -1
  104. package/web-app/dist/assets/{trending-up-BTcXLklK.js → trending-up-B44DCrt1.js} +1 -1
  105. package/web-app/dist/assets/{upload-fcHqwOxr.js → upload-4kWm33ZY.js} +1 -1
  106. package/web-app/dist/assets/{usePolling-BeJYw_4L.js → usePolling-zkUtRQG6.js} +1 -1
  107. package/web-app/dist/assets/{user-BcDwUCsO.js → user-04cEVIe2.js} +1 -1
  108. package/web-app/dist/index.html +1 -1
  109. package/dashboard/run.py +0 -63
  110. package/docs/openclaw-integration.md +0 -52
  111. package/docs/v10/HANDOFF-NEXT-WEEK.md +0 -40
  112. package/docs/v10/MORNING-BRIEF-1003.md +0 -25
  113. package/docs/v10/MORNING-BRIEF.md +0 -52
  114. package/docs/v10/TODO.md +0 -35
  115. package/mcp/resources.py +0 -351
  116. package/mcp/tools.py +0 -200
@@ -97,6 +97,20 @@ Scope hierarchy:
97
97
  - `control` includes `write` and `read`
98
98
  - `write` includes `read`
99
99
 
100
+ ### Key Rotation and Revocation
101
+
102
+ Dashboard API keys (`/api/keys`) can be rotated with a grace period during which the old key stays valid (default 24 hours, `grace_period_hours`):
103
+
104
+ ```bash
105
+ curl -X POST http://localhost:57374/api/keys/ci-pipeline/rotate \
106
+ -H "Authorization: Bearer $ADMIN_TOKEN" \
107
+ -H "Content-Type: application/json" \
108
+ -d '{"grace_period_hours": 24}'
109
+
110
+ curl -X DELETE http://localhost:57374/api/keys/ci-pipeline \
111
+ -H "Authorization: Bearer $ADMIN_TOKEN"
112
+ ```
113
+
100
114
  ### Roles
101
115
 
102
116
  Predefined roles map to common access patterns:
@@ -177,7 +191,11 @@ Running with signature verification off provides no authentication security.
177
191
  4. Provider redirects back with JWT
178
192
  5. Dashboard validates JWT and grants access
179
193
 
180
- OIDC-authenticated users receive full access scopes by default. For fine-grained control, combine OIDC with token-based authorization.
194
+ OIDC-authenticated users are NOT granted full access by default. The role is resolved from claims (see below) and its scopes are enforced through the same hierarchy as API tokens.
195
+
196
+ ### OIDC Role Claims
197
+
198
+ Role claims are read case-insensitively; values may be a string, a space-separated string or a list. Sources: the claim named by `LOKI_OIDC_ROLES_CLAIM` (dotted paths such as `realm_access.roles` work), then `roles`, `groups`, `realm_access.roles` (Keycloak) and `cognito:groups` (AWS Cognito). Only values that exactly match a built-in role name (`admin`, `operator`, `viewer`, `auditor`) grant that role; other group names fall through to `LOKI_OIDC_DEFAULT_ROLE`. When several roles match, the highest privilege wins (`admin` > `operator` > `auditor` > `viewer`). An unset or unrecognized default falls back to `viewer`, never `admin`. When PyJWT and cryptography are installed, the signature (RS256/RS384/RS512), issuer, audience and expiry are verified against the issuer JWKS.
181
199
 
182
200
  ### Mixed Mode
183
201
 
@@ -105,3 +105,11 @@ These knobs together implement the RARV-C (closure) loop: findings -> override c
105
105
  ## Project memory across runs
106
106
 
107
107
  extract_learnings_from_session (autonomy/run.sh) mirrors learnings and decisions into .loki/memory/learnings/project-*.jsonl. get_relevant_learnings adds a bounded newest-first project_memory list (15 entries, LOKI_PROJECT_MEMORY_MAX_CHARS, default 2000) to .loki/state/relevant-learnings.json at run start. LOKI_PROJECT_MEMORY=0 disables both. Test: tests/test-project-memory.sh.
108
+
109
+ ## Verdicts, composite receipts and the autonomy ladder (v11.7.0)
110
+ - Two sets: receipt verdicts (`engine10/types.ts` Verdict: VERIFIED, PARTIAL, ALREADY_SATISFIED, SPEC_CONFLICT, FAILED) and run outcomes (`engine10/output.ts` Outcome adds BUDGET_STOP, BLOCKED, STALLED, CANCELLED; exit ladder EXIT). Only receipt verdicts are sealed.
111
+ - Composite: `features/speed/seal_group.ts` composeComposite/verifyComposite (schema `loki.v10.composite/1`), sealed by `loki verify --seal-composite <run_dir>`, signed with the existing `signReceipt`, capped by `capGroupVerdict`. Workspace runs seal it after integration.json.
112
+ - Autonomy ladder: `src/policies/engine.js` allowedActions(level) (propose = none, execute = pr, operate = pr + deploy; merge never). engine10 `supervisor.ts` prGate consults `src/policies/check.js`; `loki deploy --execute` consults it for `deploy` and fails closed. `loki trust` graduationAdvice is advisory and never writes the policy file.
113
+ - Parallel units: `route.ts` maybeRunGroup defaults to `features/speed/run_group.ts`, which freezes per-unit acceptance (`features/contract.ts`), runs units, merges with `engine10/integrate.ts` and seals once via sealGroup.
114
+ - Lessons: every sealed run appends a receipt fact (`LOKI_AUTO_LEARN_PR`, default on) outside the receipt body; `implement.ts` injects lessons and relevant learnings inside the untrusted-data fence; `loki memory brief --task T` prints the same block.
115
+
@@ -4,9 +4,10 @@ Moved out of CLAUDE.md (S-78) to keep the root file short. Updated in this
4
4
  pass to remove two items that `docs/v10/DECISIONS.md` D25 and
5
5
  `docs/LOKI-10-BUILD-PROMPT.md` section 1 have superseded:
6
6
  - `git add -A` in step 4 is replaced with staging by name (repo rule, always).
7
- - Per-merge pushing is replaced by the release-train model (D25): batch
8
- approved merges locally, push once per train, freeze main until Tests, Bun
9
- Parity and Security Audit are all green on that exact SHA, then release.
7
+ - Per-merge pushing and the D25 release trains are superseded by D96 (every
8
+ approved slice is a release; see `docs/v10/OPERATING-MODEL.md`). Waiting for
9
+ Tests and Security Audit green on that exact SHA still applies to any push
10
+ made outside the merge-gate.
10
11
  "Wait for user approval before commit" from the old workflow is superseded
11
12
  by the standing authorization in `docs/LOKI-10-BUILD-PROMPT.md` section 1
12
13
  for sessions operating under that program; outside it, ask as normal.
@@ -116,13 +117,13 @@ rm -f loki-mode-*.tgz
116
117
  ```
117
118
  If any check fails, do not release; fix the root cause first.
118
119
 
119
- ### 4. Commit and land (release-train model, D25)
120
+ ### 4. Commit and land (every approved slice is a release, D96)
120
121
 
121
122
  Stage files by name (never `git add -A` or `git add .`), commit with the
122
- repo-local `asklokesh` identity, no co-author. Batch approved merges locally;
123
- push once per train (`git push origin main`); wait for Tests and
124
- Security Audit to go green on that exact SHA before bumping VERSION and
125
- releasing. GitHub Actions then creates the tag, the GitHub Release, publishes
123
+ repo-local `asklokesh` identity, no co-author. The merge-gate merges approved
124
+ slices to main and main publishes to npm `next` (D96); for a manual push
125
+ (`git push origin main`), wait for Tests and Security Audit to go green on that
126
+ exact SHA before bumping VERSION and releasing. GitHub Actions then creates the tag, the GitHub Release, publishes
126
127
  npm, builds/pushes Docker, and updates the Homebrew tap. Do not manually
127
128
  create tags.
128
129
 
@@ -6,153 +6,9 @@
6
6
 
7
7
  Loki Mode's security model follows a defense-in-depth approach. All security features are opt-in via environment variables, with sensible defaults that prioritize safety. When enterprise auth is not configured, the system operates in local-only mode with no network exposure.
8
8
 
9
- ## Authentication
9
+ ## Authentication and Authorization
10
10
 
11
- ### Token Authentication
12
-
13
- The primary authentication method uses bearer tokens prefixed with `loki_`.
14
-
15
- **Configuration:**
16
-
17
- ```bash
18
- # Enable token auth for the dashboard/control plane API
19
- export LOKI_ENTERPRISE_AUTH=true
20
- ```
21
-
22
- **Token Lifecycle:**
23
-
24
- ```bash
25
- # Create a new API key
26
- curl -X POST http://localhost:57374/api/keys \
27
- -H "Authorization: Bearer $ADMIN_TOKEN" \
28
- -H "Content-Type: application/json" \
29
- -d '{"name": "ci-pipeline", "role": "operator"}'
30
-
31
- # Rotate a key with 24-hour grace period
32
- curl -X POST http://localhost:57374/api/keys/ci-pipeline/rotate \
33
- -H "Authorization: Bearer $ADMIN_TOKEN" \
34
- -H "Content-Type: application/json" \
35
- -d '{"grace_period_hours": 24}'
36
-
37
- # Revoke a key
38
- curl -X DELETE http://localhost:57374/api/keys/ci-pipeline \
39
- -H "Authorization: Bearer $ADMIN_TOKEN"
40
- ```
41
-
42
- **SDK Usage:**
43
-
44
- Python:
45
- ```python
46
- from loki_mode_sdk import AutonomiClient
47
-
48
- client = AutonomiClient(
49
- base_url="http://localhost:57374",
50
- token="loki_your_token_here"
51
- )
52
- ```
53
-
54
- TypeScript:
55
- ```typescript
56
- import { AutonomiClient } from 'loki-mode-sdk';
57
-
58
- const client = new AutonomiClient({
59
- baseUrl: 'http://localhost:57374',
60
- token: 'loki_your_token_here'
61
- });
62
- ```
63
-
64
- ### OIDC / SSO Integration
65
-
66
- For organizations using identity providers (Okta, Auth0, Azure AD), Loki Mode supports OIDC-based authentication.
67
-
68
- **Configuration:**
69
-
70
- ```bash
71
- export LOKI_OIDC_ISSUER="https://your-idp.okta.com/oauth2/default"
72
- export LOKI_OIDC_CLIENT_ID="your-client-id"
73
- # Optional: audience override (defaults to LOKI_OIDC_CLIENT_ID)
74
- export LOKI_OIDC_AUDIENCE="your-api-audience"
75
- ```
76
-
77
- The system validates OIDC bearer tokens (presented as `Authorization: Bearer <jwt>`)
78
- against the issuer's JWKS endpoint. When PyJWT + cryptography are installed, the
79
- JWT signature is cryptographically verified (RS256/RS384/RS512) along with the
80
- issuer, audience, and expiry. Without PyJWT, tokens are rejected unless
81
- `LOKI_OIDC_SKIP_SIGNATURE_VERIFY=true` is set explicitly (insecure, local testing
82
- only).
83
-
84
- **Claims-to-roles mapping:**
85
-
86
- On a valid token, role/group claims are mapped to the four built-in Loki roles
87
- (`admin`, `operator`, `viewer`, `auditor`). The resolved role's scopes are then
88
- enforced through the same scope hierarchy used for API tokens. OIDC users are
89
- NOT granted full access by default.
90
-
91
- Recognized claim sources (case-insensitive; values may be a string,
92
- space-separated string, or list):
93
-
94
- | Claim | Provider |
95
- |-------|----------|
96
- | value of `LOKI_OIDC_ROLES_CLAIM` (supports dotted paths, e.g. `realm_access.roles`) | configurable |
97
- | `roles` | generic |
98
- | `groups` | generic |
99
- | `realm_access.roles` | Keycloak |
100
- | `cognito:groups` | AWS Cognito |
101
-
102
- Only claim values that exactly match a built-in role name (`admin`, `operator`,
103
- `viewer`, `auditor`) grant that role. Arbitrary group names (common in `groups`
104
- or `cognito:groups`) do not map to a role and fall through to the default.
105
-
106
- When multiple recognized roles are present, the highest-privilege match wins
107
- (precedence: `admin` > `operator` > `auditor` > `viewer`).
108
-
109
- **Default role (when no recognized role claim is present):**
110
-
111
- ```bash
112
- # Defaults to the least-privileged role (viewer). Never admin.
113
- export LOKI_OIDC_DEFAULT_ROLE="viewer"
114
- ```
115
-
116
- If `LOKI_OIDC_DEFAULT_ROLE` is unset or set to an unrecognized value, it falls
117
- back to `viewer`. The default is never `admin` / full access.
118
-
119
- ## Authorization
120
-
121
- ### Role-Based Access Control
122
-
123
- Four built-in roles provide scoped access:
124
-
125
- | Role | Scope | Permissions |
126
- |------|-------|-------------|
127
- | `admin` | Full control | Create/delete tenants, manage API keys, configure policies, all operator/viewer/auditor permissions |
128
- | `operator` | Execution | Start/cancel runs, create/update projects and tasks, read all resources |
129
- | `viewer` | Read-only | Read projects, runs, tasks, status. No write or delete operations |
130
- | `auditor` | Audit access | Read audit logs, verify chain integrity, generate compliance reports. No execution permissions |
131
-
132
- **Scope Hierarchy:**
133
-
134
- ```
135
- admin > operator > viewer
136
- admin > auditor
137
- ```
138
-
139
- Admin inherits all permissions. Operator inherits viewer permissions. Auditor is a separate branch with read-only access to audit data.
140
-
141
- **API Key Scopes:**
142
-
143
- API keys are created with a role assignment:
144
-
145
- ```bash
146
- # Create a viewer key for monitoring dashboards
147
- curl -X POST http://localhost:57374/api/keys \
148
- -H "Authorization: Bearer $ADMIN_TOKEN" \
149
- -d '{"name": "grafana-reader", "role": "viewer"}'
150
-
151
- # Create an operator key for CI pipelines
152
- curl -X POST http://localhost:57374/api/keys \
153
- -H "Authorization: Bearer $ADMIN_TOKEN" \
154
- -d '{"name": "github-actions", "role": "operator"}'
155
- ```
11
+ Token auth (`LOKI_ENTERPRISE_AUTH`), key rotation with a 24-hour grace period, OIDC/SSO claim mapping and the default viewer role are documented in [authentication.md](../authentication.md). Roles (`admin`, `operator`, `viewer`, `auditor`), scope hierarchy and API key scopes are in [authorization.md](../authorization.md).
156
12
 
157
13
  ## API Security
158
14
 
@@ -221,94 +77,7 @@ This is a deliberate fail-closed design. Invalid or missing signatures always re
221
77
 
222
78
  ## Audit Logging
223
79
 
224
- ### Hash-Chained Audit Logs
225
-
226
- Audit logs use SHA-256 hash chains for tamper evidence. Each entry's hash depends on the previous entry's hash, creating an immutable chain from the genesis record.
227
-
228
- **JavaScript Audit (Agent Actions):**
229
-
230
- ```
231
- .loki/audit/audit.jsonl
232
- ```
233
-
234
- Each entry:
235
- ```json
236
- {
237
- "seq": 42,
238
- "timestamp": "2026-02-21T10:00:00.000Z",
239
- "who": "agent-1",
240
- "what": "file_write",
241
- "where": "src/app.js",
242
- "why": "implement feature",
243
- "previousHash": "abc123...",
244
- "hash": "def456..."
245
- }
246
- ```
247
-
248
- **Python Audit (Dashboard API):**
249
-
250
- ```
251
- ~/.loki/dashboard/audit/audit-2026-02-21.jsonl
252
- ```
253
-
254
- Each entry:
255
- ```json
256
- {
257
- "timestamp": "2026-02-21T10:00:00.000Z",
258
- "action": "create",
259
- "resource_type": "project",
260
- "resource_id": "proj-1",
261
- "user_id": "admin",
262
- "success": true,
263
- "_integrity_hash": "abc123..."
264
- }
265
- ```
266
-
267
- ### Chain Verification
268
-
269
- JavaScript:
270
- ```javascript
271
- const audit = require('./src/audit');
272
- audit.init('/path/to/project');
273
- const result = audit.verifyChain();
274
- // { valid: true, entries: 1542, brokenAt: null, error: null }
275
- ```
276
-
277
- Python:
278
- ```python
279
- from dashboard.audit import verify_log_integrity
280
- result = verify_log_integrity("/path/to/audit-2026-02-21.jsonl")
281
- # {"valid": True, "entries_checked": 1542, "first_tampered_line": None}
282
- ```
283
-
284
- API:
285
- ```bash
286
- curl http://localhost:57374/api/audit/verify \
287
- -H "Authorization: Bearer $TOKEN"
288
- ```
289
-
290
- ### Syslog Forwarding
291
-
292
- Forward audit events to a centralized syslog server for SIEM integration:
293
-
294
- ```bash
295
- export LOKI_AUDIT_SYSLOG_HOST="syslog.company.com"
296
- export LOKI_AUDIT_SYSLOG_PORT="514" # default
297
- export LOKI_AUDIT_SYSLOG_PROTO="udp" # or "tcp"
298
- ```
299
-
300
- Security-relevant actions (`delete`, `kill`, `stop`, `login`, `logout`, `create_token`, `revoke_token`) are logged at `WARNING` level. All other actions are logged at `INFO` level.
301
-
302
- Syslog forwarding is fire-and-forget -- failures never block the main audit write path.
303
-
304
- ### Log Rotation
305
-
306
- ```bash
307
- export LOKI_AUDIT_MAX_SIZE_MB="10" # Rotate when file exceeds this size
308
- export LOKI_AUDIT_MAX_FILES="10" # Keep this many rotated files
309
- ```
310
-
311
- Date-based log files (`audit-YYYY-MM-DD.jsonl`) are automatically rotated when they exceed the size limit. Old files are removed when the count exceeds the maximum.
80
+ Hash-chained audit logs, chain verification, syslog forwarding (`LOKI_AUDIT_SYSLOG_*`) and rotation (`LOKI_AUDIT_MAX_SIZE_MB`, `LOKI_AUDIT_MAX_FILES`) are documented in [audit-logging.md](../audit-logging.md).
312
81
 
313
82
  ## Data Residency
314
83
 
@@ -11,3 +11,14 @@ From a URL (needs `SENTRY_AUTH_TOKEN`; without it nothing is fetched):
11
11
  SENTRY_AUTH_TOKEN=... loki start https://sentry.io/organizations/acme/issues/4001/
12
12
 
13
13
  The token is sent only to the Sentry host through a curl config on stdin, never on argv, and is never printed. Implementation: `autonomy/issue-providers.sh` (`normalize_sentry_json`).
14
+
15
+ ## Webhook: self-healing entry point
16
+
17
+ `autonomy/trigger-server.py` accepts `POST /sentry`, a Sentry integration webhook authenticated by Sentry's own HMAC (`sentry-hook-signature`, hex HMAC-SHA256 of the body).
18
+
19
+ 1. Set the client secret: `LOKI_SENTRY_WEBHOOK_SECRET` (or `SENTRY_CLIENT_SECRET`). It must differ from the GitHub webhook secret and the API token. With no secret the route answers 503 and nothing is enqueued.
20
+ 2. Start the server: `python3 autonomy/trigger-server.py --port 8080` and point the Sentry alert webhook at `/sentry`.
21
+ 3. A valid alert is normalized with `normalize_sentry_json` and enqueues `loki start "<brief>" --pr --detach`. A bad signature gets 401.
22
+
23
+ The output is a receipt-gated PR only, never a deploy. Whether the PR opens is still decided by the policy file (`autonomy`).
24
+