blun-king-cli 9.1.485 → 9.1.488

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 (99) hide show
  1. package/LIESMICH.txt +33 -17
  2. package/README.md +20 -8
  3. package/agent-spine-plugin/.claude-plugin/marketplace.json +20 -0
  4. package/agent-spine-plugin/.claude-plugin/plugin.json +14 -0
  5. package/agent-spine-plugin/.codex-plugin/plugin.json +34 -0
  6. package/agent-spine-plugin/.mcp.json +8 -0
  7. package/agent-spine-plugin/CHANGELOG.md +277 -0
  8. package/agent-spine-plugin/LICENSE +186 -0
  9. package/agent-spine-plugin/README.md +342 -0
  10. package/agent-spine-plugin/assets/agentspine-banner.svg +32 -0
  11. package/agent-spine-plugin/bin/agentspine-mcp.js +4 -0
  12. package/agent-spine-plugin/bin/agentspine.js +7 -0
  13. package/agent-spine-plugin/blun.plugin.json +33 -0
  14. package/agent-spine-plugin/docs/acceptance.md +61 -0
  15. package/agent-spine-plugin/docs/architecture.md +183 -0
  16. package/agent-spine-plugin/docs/attention.md +121 -0
  17. package/agent-spine-plugin/docs/automatic-continuity.md +79 -0
  18. package/agent-spine-plugin/docs/channel-runtime.md +92 -0
  19. package/agent-spine-plugin/docs/coordination.md +138 -0
  20. package/agent-spine-plugin/docs/feed-transport.md +99 -0
  21. package/agent-spine-plugin/docs/gateway-runtime.md +109 -0
  22. package/agent-spine-plugin/docs/harness-reference.md +45 -0
  23. package/agent-spine-plugin/docs/host-integration.md +129 -0
  24. package/agent-spine-plugin/docs/https-transport.md +116 -0
  25. package/agent-spine-plugin/docs/learning.md +98 -0
  26. package/agent-spine-plugin/docs/object-transport.md +93 -0
  27. package/agent-spine-plugin/docs/peer-transport.md +88 -0
  28. package/agent-spine-plugin/docs/preservation-contract.md +53 -0
  29. package/agent-spine-plugin/docs/quality-gates.md +50 -0
  30. package/agent-spine-plugin/docs/relationships.md +67 -0
  31. package/agent-spine-plugin/docs/releasing.md +83 -0
  32. package/agent-spine-plugin/docs/roadmap.md +307 -0
  33. package/agent-spine-plugin/docs/selfstarter.md +88 -0
  34. package/agent-spine-plugin/docs/session-briefing.md +74 -0
  35. package/agent-spine-plugin/docs/shared-memory.md +259 -0
  36. package/agent-spine-plugin/docs/source-roots.md +86 -0
  37. package/agent-spine-plugin/docs/sqlite-transport.md +76 -0
  38. package/agent-spine-plugin/hooks/hooks.json +95 -0
  39. package/agent-spine-plugin/hooks/version.json +5 -0
  40. package/agent-spine-plugin/package.json +69 -0
  41. package/agent-spine-plugin/scripts/check-hosts.js +193 -0
  42. package/agent-spine-plugin/scripts/check-install.js +551 -0
  43. package/agent-spine-plugin/scripts/check-syntax.js +29 -0
  44. package/agent-spine-plugin/scripts/github-actions.js +11 -0
  45. package/agent-spine-plugin/scripts/release-check.js +127 -0
  46. package/agent-spine-plugin/scripts/run-acceptance.js +19 -0
  47. package/agent-spine-plugin/scripts/run-checks.js +46 -0
  48. package/agent-spine-plugin/scripts/run-tests-hermetic.js +73 -0
  49. package/agent-spine-plugin/skill/SKILL.md +76 -0
  50. package/agent-spine-plugin/skills/agent-spine/SKILL.md +84 -0
  51. package/agent-spine-plugin/spine-example/1-identity.md +12 -0
  52. package/agent-spine-plugin/spine-example/2-voice.md +6 -0
  53. package/agent-spine-plugin/spine-example/3-conduct.md +8 -0
  54. package/agent-spine-plugin/spine-example/4-history.md +4 -0
  55. package/agent-spine-plugin/src/cli.js +950 -0
  56. package/agent-spine-plugin/src/hook.js +646 -0
  57. package/agent-spine-plugin/src/index.js +84 -0
  58. package/agent-spine-plugin/src/lib/acceptance.js +294 -0
  59. package/agent-spine-plugin/src/lib/attention.js +755 -0
  60. package/agent-spine-plugin/src/lib/audit.js +308 -0
  61. package/agent-spine-plugin/src/lib/authentication.js +513 -0
  62. package/agent-spine-plugin/src/lib/briefing.js +305 -0
  63. package/agent-spine-plugin/src/lib/catalog.js +142 -0
  64. package/agent-spine-plugin/src/lib/channel-runtime.js +665 -0
  65. package/agent-spine-plugin/src/lib/context.js +154 -0
  66. package/agent-spine-plugin/src/lib/continuity.js +333 -0
  67. package/agent-spine-plugin/src/lib/coordination.js +577 -0
  68. package/agent-spine-plugin/src/lib/documents.js +199 -0
  69. package/agent-spine-plugin/src/lib/feed-transport.js +497 -0
  70. package/agent-spine-plugin/src/lib/filesystem-retry.js +27 -0
  71. package/agent-spine-plugin/src/lib/gateway-runtime.js +760 -0
  72. package/agent-spine-plugin/src/lib/graph.js +284 -0
  73. package/agent-spine-plugin/src/lib/https-transport.js +392 -0
  74. package/agent-spine-plugin/src/lib/indexed-memory-offline.js +40 -0
  75. package/agent-spine-plugin/src/lib/indexed-memory.js +281 -0
  76. package/agent-spine-plugin/src/lib/learning.js +645 -0
  77. package/agent-spine-plugin/src/lib/object-transport.js +206 -0
  78. package/agent-spine-plugin/src/lib/paths.js +60 -0
  79. package/agent-spine-plugin/src/lib/peer-transport.js +283 -0
  80. package/agent-spine-plugin/src/lib/persona-runtime.js +503 -0
  81. package/agent-spine-plugin/src/lib/runtime.js +13 -0
  82. package/agent-spine-plugin/src/lib/selfstarter.js +819 -0
  83. package/agent-spine-plugin/src/lib/sharing.js +969 -0
  84. package/agent-spine-plugin/src/lib/source-roots.js +451 -0
  85. package/agent-spine-plugin/src/lib/sqlite-transport.js +501 -0
  86. package/agent-spine-plugin/src/lib/telegram-adapter.js +119 -0
  87. package/agent-spine-plugin/src/lib/voice-runtime.js +39 -0
  88. package/agent-spine-plugin/src/mcp.js +526 -0
  89. package/agent-spine-plugin/src/version.js +1 -0
  90. package/agent-spine-plugin/src/worker.js +178 -0
  91. package/bin/identity-context-policy.cjs +51 -1
  92. package/bin/launcher-runtime.js +22 -1
  93. package/bin/plugin-bootstrap.js +68 -27
  94. package/bin/startup-preferences.cjs +130 -0
  95. package/bin/turn-thinking-policy.cjs +5 -4
  96. package/bin/turn-tool-performance-policy.cjs +15 -2
  97. package/bin/update-notice.js +104 -43
  98. package/blun.mjs +108 -32
  99. package/package.json +2 -1
@@ -0,0 +1,138 @@
1
+ # Delegation and coordination
2
+
3
+ AgentSpine can retain tasks, open threads, and handoffs across sessions without turning memory into an authorization system. Work state and delegation policy are deliberately different files, different authorities, and different tool surfaces.
4
+
5
+ ## Separation by construction
6
+
7
+ ```mermaid
8
+ flowchart LR
9
+ subgraph Context["Untrusted context"]
10
+ M["Markdown + memory"]
11
+ R["Relationships"]
12
+ L["Learning + attention"]
13
+ end
14
+ subgraph Policy["Explicit local owner policy"]
15
+ P["delegation-policy.json"]
16
+ end
17
+ D["Default-deny decision"]
18
+ T["coordination.json"]
19
+ H["Host authorization"]
20
+ M -. "never grants" .-> D
21
+ R -. "never grants" .-> D
22
+ L -. "never grants" .-> D
23
+ P --> D
24
+ D -->|"coordination allowed"| T
25
+ T -. "never grants" .-> H
26
+ P -. "does not grant" .-> H
27
+ ```
28
+
29
+ `delegation-policy.json` contains only explicit local grants for AgentSpine task coordination. `coordination.json` contains context-only work records and append-only prior versions. Neither file grants host tool access, file or network access, production rights, spending authority, credentials, or policy exceptions. Those remain under the host and operating environment. The optional self-starter uses a third, separate `execution-policy.json`; see [rights-bound self-starter](selfstarter.md). A coordination task alone never creates an execution grant.
30
+
31
+ A `responsible-for`, `reports-to`, or `works-with` relationship describes the team. It never satisfies a delegation check. A sentence in `SOUL.md`, `AGENTS.md`, `CLAUDE.md`, memory, accepted learning, a task, or an MCP response also cannot create a grant.
32
+
33
+ ## Default-deny delegation
34
+
35
+ The supported coordination actions are:
36
+
37
+ - `assign` — create a task assigned to another entity;
38
+ - `reassign` — change an existing assignee, including assignment by a manager;
39
+ - `manage` — change another entity's task content or non-terminal status;
40
+ - `complete` — complete another entity's task;
41
+ - `cancel` — cancel another entity's task.
42
+
43
+ Creating an unassigned thread or assigning work to oneself is self-coordination and needs no delegation grant. An assignee may manage their own task. Every cross-entity action fails closed unless the actor, action, and target match an active explicit grant.
44
+
45
+ Inspect the decision before acting:
46
+
47
+ ```bash
48
+ agentspine delegation-check agent:lead \
49
+ --action assign \
50
+ --target agent:builder \
51
+ --root /path/to/project \
52
+ --json
53
+ ```
54
+
55
+ The MCP server exposes `check_delegation`, but intentionally exposes no policy grant or revoke tool. This prevents an agent from widening the same policy it is expected to obey.
56
+
57
+ ## Owner-controlled policy changes
58
+
59
+ The CLI is the local administration surface:
60
+
61
+ ```bash
62
+ agentspine delegation-grant agent:lead \
63
+ --id grant:lead-builders \
64
+ --actions assign,reassign,manage \
65
+ --targets agent:builder \
66
+ --reason "Approved for local project coordination" \
67
+ --confirm-local-policy \
68
+ --root /path/to/project
69
+
70
+ agentspine delegation-revoke grant:lead-builders \
71
+ --reason "Project handoff completed" \
72
+ --confirm-local-policy \
73
+ --root /path/to/project
74
+ ```
75
+
76
+ `--confirm-local-policy` is an integration attestation, not authentication. A host or wrapper must bind it to a genuine local owner action and must not infer it from conversation, memory, Markdown, another agent, or a task. Grant IDs are immutable. Revocation retains the prior grant in policy history so existing assignment snapshots remain auditable, while future actions are denied.
77
+
78
+ ## Tasks, open threads, and handoffs
79
+
80
+ ```mermaid
81
+ stateDiagram-v2
82
+ [*] --> open
83
+ open --> in_progress
84
+ in_progress --> blocked
85
+ blocked --> in_progress
86
+ open --> completed
87
+ in_progress --> completed
88
+ blocked --> completed
89
+ open --> cancelled
90
+ in_progress --> cancelled
91
+ blocked --> cancelled
92
+ ```
93
+
94
+ Create and inspect work:
95
+
96
+ ```bash
97
+ agentspine task-create task:release \
98
+ --actor agent:lead \
99
+ --assignee agent:builder \
100
+ --kind handoff \
101
+ --title "Prepare the release candidate" \
102
+ --summary "Run the documented release gates" \
103
+ --privacy shared \
104
+ --root /path/to/project
105
+
106
+ agentspine task-update task:release \
107
+ --actor agent:builder \
108
+ --status in-progress \
109
+ --note "Validation is running" \
110
+ --root /path/to/project
111
+
112
+ agentspine tasks /path/to/project --assignee agent:builder --json
113
+ ```
114
+
115
+ The MCP equivalents are `create_task`, `update_task`, and `task_context`. Returned context omits the internal delegation snapshot. Each mutation retains the complete previous task value before replacing the active view. New information therefore changes current relevance without erasing what was previously understood.
116
+
117
+ Permanent task deletion is CLI-only and requires the same explicit local confirmation marker. It removes the active record and all retained versions; use it for privacy removal, not routine completion.
118
+
119
+ ## Privacy and groups
120
+
121
+ Tasks use `private`, `shared`, or `group` scope. Group tasks require a known group and visible `member-of` edges for the creator and assignee. Reads require the exact same group ID. `includePrivate` cannot bypass a missing or different group audience.
122
+
123
+ Lifecycle hooks have no private or group audience. They inject only the number and kinds of open shared coordination records—never titles, summaries, notes, assignees, or delegation policy. The agent must explicitly request relevant context.
124
+
125
+ ## Integrity and concurrency
126
+
127
+ Both state files are private external project state, capped at 5 MiB, written with restrictive file mode and atomic replacement. Cross-process locks serialize policy changes and task mutations. A task mutation holds the policy read lock until its coordination write completes, so revocation cannot race an assignment into existence.
128
+
129
+ All decision and mutation paths validate current state before use. Unknown entities, secrets, forged provenance, invalid assignment snapshots, malformed JSON, or inconsistent policy cause a fail-closed error. Damaged files are reported by `agentspine audit` and are never overwritten automatically.
130
+
131
+ ## Deliberate limits
132
+
133
+ - AgentSpine coordinates records without execution authority. The optional self-starter is the sole narrow exception and requires a separate current exact execution grant for every lifecycle effect.
134
+ - It does not send Telegram, email, chat, or notification messages.
135
+ - It does not authenticate the human operating a shell.
136
+ - It does not synchronize policy or tasks across machines.
137
+ - It does not treat organizational relationships as an access-control list.
138
+ - It does not replace host approvals, operating-system permissions, or an external policy engine.
@@ -0,0 +1,99 @@
1
+ # Signed mutable feed transport
2
+
3
+ AgentSpine can discover successive immutable HTTPS snapshots through one signed, compare-and-swap feed. The feed is provider-neutral and deliberately narrow: it is a bounded hash-chain window of snapshot references, not a remote database, command queue, permission source, or replacement for local review.
4
+
5
+ ```mermaid
6
+ sequenceDiagram
7
+ participant P as Local publisher
8
+ participant O as Immutable objects
9
+ participant F as Mutable feed
10
+ participant R as Local receiver
11
+ P->>O: Create snapshot object<br/>If-None-Match: *
12
+ P->>F: GET signed chain + strong ETag
13
+ P->>P: Append entry and sign entire window
14
+ P->>F: PUT new chain<br/>If-Match: previous ETag
15
+ F-->>P: 200/204 or 412 conflict
16
+ P->>F: Verify exact read-back
17
+ R->>F: GET signed chain + strong ETag
18
+ R->>R: Verify trusted key, chain and local receipt
19
+ R->>O: GET content-addressed snapshot
20
+ R->>R: Verify snapshot and import to quarantine
21
+ R->>R: Persist new receipt with prior receipt in history
22
+ ```
23
+
24
+ ## Publish
25
+
26
+ The directory adapter must already be authenticated, and the named signer must remain stable for the lifetime of one feed:
27
+
28
+ ```bash
29
+ export AGENTSPINE_FEED_TOKEN='deployment-supplied-value'
30
+
31
+ agentspine share-feed-publish /srv/agent-memory/team-alpha \
32
+ --root /path/to/publisher-project \
33
+ --base https://memory.example.org/agentspine/team-alpha \
34
+ --feed feed:team-alpha \
35
+ --signer signer:team-alpha \
36
+ --id snapshot:team-alpha-2026-08-28-01 \
37
+ --token-env AGENTSPINE_FEED_TOKEN \
38
+ --confirm-local-share
39
+ ```
40
+
41
+ Publication first creates and verifies the immutable object. It then reads the current feed, appends one entry, signs the entire retained window, updates using the previous strong ETag, and verifies an exact read-back. A `412` is a normal concurrency conflict: no retry is hidden, and the caller must fetch the winning feed before deciding whether to publish another snapshot.
42
+
43
+ The stable feed URL is derived from the SHA-256 digest of `feedId`:
44
+
45
+ ```text
46
+ https://memory.example.org/agentspine/team-alpha/feeds/{sha256(feedId)}.json
47
+ ```
48
+
49
+ ## Pull
50
+
51
+ The receiver explicitly trusts the publisher's exported Ed25519 identity before pulling:
52
+
53
+ ```bash
54
+ agentspine share-feed-pull \
55
+ --root /path/to/receiver-project \
56
+ --base https://memory.example.org/agentspine/team-alpha \
57
+ --feed feed:team-alpha \
58
+ --token-env AGENTSPINE_FEED_TOKEN
59
+ ```
60
+
61
+ The first observation may join an established feed at its current tip. Every later pull must prove continuity from the locally stored receipt. A lower sequence is a rollback. A different entry at an observed sequence is equivocation. A newer feed whose bounded window no longer contains the observed tip is a continuity gap and fails closed instead of silently skipping history.
62
+
63
+ The feed retains at most 256 references. Receivers that may remain offline for more than 255 publications should poll less sparsely or begin a new explicitly trusted feed after operator review. Feed signer rotation also requires a new `feedId`; this avoids treating a same-name key as the old identity.
64
+
65
+ `share-feed-state` shows local receipts and retained prior receipts. There is intentionally no automatic reset command. Removing or replacing rollback protection is an operator decision outside the agent-controlled CLI workflow.
66
+
67
+ ## Server contract
68
+
69
+ For a new feed, the service must atomically create the resource:
70
+
71
+ ```http
72
+ PUT /agentspine/team-alpha/feeds/{feed-id-sha256}.json
73
+ Content-Type: application/vnd.agentspine.feed+json
74
+ If-None-Match: *
75
+ ```
76
+
77
+ It returns `201` or `204`, then serves the exact document with a strong ETag. Updates require:
78
+
79
+ ```http
80
+ PUT /agentspine/team-alpha/feeds/{feed-id-sha256}.json
81
+ Content-Type: application/vnd.agentspine.feed+json
82
+ If-Match: "the-etag-returned-by-the-last-get"
83
+ ```
84
+
85
+ The service returns `200` or `204` only when that exact ETag still matches. It returns `412` without changing the feed after a competing update. Weak ETags are rejected because they cannot protect byte-exact compare-and-swap semantics. Responses are uncompressed JSON, no larger than 256 KiB, and redirects are never followed.
86
+
87
+ The object endpoints continue to implement the create-only contract in [immutable HTTPS objects](object-transport.md). A production service should independently authenticate writers, verify request sizes and preconditions atomically, rate-limit access, and avoid logging bearer values or feed bodies.
88
+
89
+ ## Security and authority boundary
90
+
91
+ - TLS, vetted and pinned DNS, SSRF protection, strict timeouts, no redirects, exact limits, and environment-only bearer tokens apply to both feed reads and writes.
92
+ - Every network write requires explicit local owner confirmation. Private-network access requires the same confirmation plus an explicit opt-in.
93
+ - The signed feed authenticates one configured origin and detects history changes. It does not approve any referenced claim.
94
+ - The referenced snapshot is independently digest-checked and signature-checked, then imported through the existing quarantine. A second local user review remains mandatory before context use.
95
+ - Feed receipts and their history live outside the scanned project and are checked by the ten-gate audit. Corrupt state is never overwritten automatically.
96
+ - Feed payloads, receipts, snapshots, bearer authentication, signatures, and remote service responses always remain `context-only`. They cannot grant permissions, delegation, production access, spending rights, or policy exceptions.
97
+ - Feed publication, pulling, endpoint selection, tokens, and receipt mutation are absent from MCP and hooks.
98
+
99
+ Existing `AGENTS.md`, `CLAUDE.md`, `SOUL.md`, `MEMORY.md`, and other discovered Markdown files are never transported or modified by this layer.
@@ -0,0 +1,109 @@
1
+ # Durable gateway worker
2
+
3
+ AgentSpine `0.8.0` includes an optional long-running worker that closes the gap between a channel message and an actual Claude Code or Codex run. While the worker is running, it polls configured Telegram accounts, authenticates every accepted update against an exact local channel binding, starts one leased agent lane, and delivers one idempotent reply to the original chat, topic, and message.
4
+
5
+ The worker is not exposed through MCP and is never started by remembered text. Its policy defaults to disabled; the owner enables it once with `agentspine gateway-control /path/to/project --enabled true --confirm-local-gateway`. An operating-system service manager may then supervise it after the owner configures its environment and command line.
6
+
7
+ ## Automatic persona roster
8
+
9
+ Set `AGENTSPINE_PERSONA_ROSTER_FILE` or pass `--persona-roster` to the worker. The path must be absolute, point to a regular non-symlink JSON file outside the agent project, and remain below 1 MiB. The file uses this envelope:
10
+
11
+ ```json
12
+ {
13
+ "schema": "agentspine.persona-roster/v1",
14
+ "revision": 1,
15
+ "observedAt": "2032-01-01T00:00:00.000Z",
16
+ "bindings": [
17
+ {
18
+ "id": "persona-binding:franz",
19
+ "authenticator": "host-manifest",
20
+ "issuer": "host:local",
21
+ "tenantId": "tenant:blun",
22
+ "host": "codex",
23
+ "profileId": "profile:franz",
24
+ "subjectId": "subject:franz",
25
+ "kind": "agent",
26
+ "displayName": "Franz",
27
+ "sourceBinding": ".codex/agents/franz.md",
28
+ "groupId": "group:engineering"
29
+ }
30
+ ]
31
+ }
32
+ ```
33
+
34
+ Every tick synchronizes authenticated identities before any work is reconciled. New bindings append `join` events; renames retain the stable persona ID; omitted members in the same explicit roster scope become `left`; `"deactivated": true` records a distinct deactivation; reappearance appends `rejoin`. Names, Markdown, memory, and chat text cannot create identity or rights. Existing source files are never changed.
35
+
36
+ The same approved envelope may contain `nativeDiscovery` scopes. AgentSpine then checks only the officially documented direct agent-manifest directories: Claude Code `~/.claude/agents/` or `<project>/.claude/agents/`, and Codex `~/.codex/agents/` or `<project>/.codex/agents/`. `CLAUDE_CONFIG_DIR` and `CODEX_HOME` replace only their matching user scope. Each scope fixes issuer, tenant, profile, agent/bot kind, and optional group; those authenticated scope fields plus the exact source binding form the stable identity. The manifest contributes only its declared display name and an exact source descriptor; its instructions remain host-native context. Direct regular `.md` or `.toml` files are bounded to 128 entries and 256 KiB each; symlinks and files exchanged during a read fail closed. No other home or project directory is enumerated. See the official [Claude Code custom subagent locations](https://code.claude.com/docs/en/sub-agents) and [Codex custom agent locations](https://developers.openai.com/codex/agent-configuration/subagents).
37
+
38
+ ```json
39
+ {
40
+ "nativeDiscovery": [
41
+ {
42
+ "id": "native:claude:user",
43
+ "host": "claude",
44
+ "scope": "user",
45
+ "issuer": "host:local",
46
+ "tenantId": "tenant:blun",
47
+ "profileId": "profile:franz",
48
+ "kind": "agent",
49
+ "groupId": "group:engineering"
50
+ }
51
+ ]
52
+ }
53
+ ```
54
+
55
+ A one-shot manual validation is available through:
56
+
57
+ ```bash
58
+ agentspine persona-sync /path/to/project \
59
+ --roster /absolute/path/to/roster.json \
60
+ --confirm-local-persona
61
+ ```
62
+
63
+ ## Telegram and host runner
64
+
65
+ An active Telegram channel binding needs `receive` and `reply`, an ingress HMAC environment variable, and an outbound Bot API token environment variable. The worker polls only accounts present in current bindings. Unknown chats, topics, senders, and non-text updates do not enter the queue.
66
+
67
+ `AGENTSPINE_HOST_RUNNER` must be an absolute locally approved executable path. AgentSpine invokes it with no shell and a minimized environment. One bounded JSON request arrives on stdin. For channel work it contains the exact host, profile, project root, agent scope, session key, and `agent_spine_channel_event` reference needed to start the native lifecycle hook. The message text itself is loaded by that hook from the authenticated channel state.
68
+
69
+ When the runner starts the selected stock CLI, it maps that request to the child process environment: `AGENTSPINE_GATEWAY_CONTEXT=agentspine.gateway-start/v1`, `AGENTSPINE_ENTITY_ID`, `AGENTSPINE_PROJECT_ID`, optional `AGENTSPINE_GROUP_ID`, plus `AGENTSPINE_CHANNEL_EVENT_ID` and `AGENTSPINE_CHANNEL_PROVIDER`. The native `SessionStart` payload does not need proprietary fields; the trusted AgentSpine hook reads this exact environment bridge and then leases the referenced event. The runner must not pass provider tokens or the ingress HMAC secret to the host process.
70
+
71
+ The runner returns one bounded JSON result:
72
+
73
+ ```json
74
+ {
75
+ "text": "Die geprüfte Antwort.",
76
+ "checkpoint": { "step": "verified" },
77
+ "completed": true
78
+ }
79
+ ```
80
+
81
+ Start one diagnostic tick:
82
+
83
+ ```bash
84
+ agentspine gateway-control /path/to/project \
85
+ --enabled true --confirm-local-gateway
86
+
87
+ AGENTSPINE_TELEGRAM_TOKEN='…' \
88
+ AGENTSPINE_TELEGRAM_INGRESS_SECRET='at-least-32-bytes…' \
89
+ AGENTSPINE_HOST_RUNNER='/absolute/path/to/approved-runner' \
90
+ agentspine-worker --root /path/to/project \
91
+ --persona-roster /absolute/path/to/roster.json --once
92
+ ```
93
+
94
+ Remove `--once` under a service manager for continuous operation. AgentSpine does not silently install a daemon, persist provider tokens, or approve Codex/Claude executable hooks. Codex still requires the current hook definition to be visible and trusted in `/hooks`.
95
+
96
+ ## Goals, attention, and recovery
97
+
98
+ An agent without a queued message or an owner-assigned goal reports `idle/needs-goal`. `goal-assign` creates one exact focused goal for an active authenticated agent. Promise, resolved-blocker, deadline, assignment, follow-up, and direct-message wakes share bounded per-agent lanes. Each effect rechecks current policy, identity, group, route, and kill-switch state.
99
+
100
+ Queue leases expire safely, retries are bounded, and the worker wakes on relevant desired-state files or a capped timer without watching its own runtime writes. Prepared and demonstrably effect-free failed deliveries resume after restart. A revoked reply capability or exhausted no-effect retry budget becomes `dead-letter`; ambiguous sends become `delivery-unknown` and are never automatically replayed. Already delivered outbox entries are never sent twice. Checkpoints reject secrets and authority-shaped content. The ten-gate audit replays persona events, gateway history, receipts, lanes, queue IDs, delivery IDs, independent health heartbeats, and current authority markers.
101
+
102
+ The worker can be stopped locally at any time:
103
+
104
+ ```bash
105
+ agentspine gateway-control /path/to/project \
106
+ --kill-switch true --confirm-local-gateway
107
+ ```
108
+
109
+ Memory, learning, relationships, persona files, goals, and model output cannot grant channel, tool, process, or execution permission.
@@ -0,0 +1,45 @@
1
+ # Harness reference study
2
+
3
+ This architecture input was reviewed on 2026-08-29 against primary sources only. No upstream code, brand material, or persona text is included in AgentSpine.
4
+
5
+ ## Reviewed snapshots
6
+
7
+ - OpenClaw `2026.8.1`, commit [`fb7822e1`](https://github.com/openclaw/openclaw/commit/fb7822e1d3333c9417131fa3514000a6a17e6dbd): [multi-agent routing](https://docs.openclaw.ai/concepts/multi-agent), [Gateway protocol](https://docs.openclaw.ai/gateway/protocol), and [cron jobs](https://docs.openclaw.ai/automation/cron-jobs).
8
+ - NousResearch Hermes Agent `0.20.6`, commit [`1c5ee581`](https://github.com/NousResearch/hermes-agent/commit/1c5ee5815fe5a3913530ba9d803b5b60bc633766): [cron guide](https://hermes-agent.nousresearch.com/docs/user-guide/features/cron), [messaging overview](https://hermes-agent.nousresearch.com/docs/user-guide/messaging), and [Telegram guide](https://hermes-agent.nousresearch.com/docs/user-guide/messaging/telegram).
9
+
10
+ ## Architecture comparison
11
+
12
+ | Pattern | Übernehmen | An AgentSpine anpassen | Bewusst nicht übernehmen |
13
+ |---|---|---|---|
14
+ | Long-lived local gateway | Durable event ownership, health, wake-up, and recovery outside conversational turns | Optional provider-neutral worker; it does not replace the model host or tool registry | No silent network listener, hidden installation, or MCP administration |
15
+ | Agent isolation | Per-agent lane, state, session key, and exact channel-account binding | Separate tenant, profile, project, group, chat, and thread in every route | No fallback from one agent's private session, credentials, or memory into another |
16
+ | Scheduler | Persistent desired state, startup reconciliation, targeted event wake, and independent ticker health | Goals and obligations enter one bounded priority queue; timer is capped and has no busy loop | No prompt-created cron authority and no model-controlled scheduler policy |
17
+ | Isolated scheduled run | Fresh bounded host run with a terminal result and durable checkpoint | Return only the safe result/checkpoint to the responsible main context | No global transcript import and no private cross-session history mixing |
18
+ | Telegram | Stable numeric identities, explicit binding, origin-preserving replies, allowlist-style ingress | HMAC-normalized `agentspine.channel-event/v1`, exact tenant/account/chat/thread/sender/agent scope | No name-based identity, inferred recipient, broad bot token access, or send capability from text |
19
+ | Persona and voice | Per-agent persona source and stable named-bot separation | Authenticated roster metadata plus a bounded context-only `voiceBrief` | No persona file copying, invented feelings, dependency language, or authority from `SOUL.md` |
20
+ | Delivery | Idempotent outbox, receipts, retry, and terminal failure | Ambiguous sends become `delivery-unknown` and require reconciliation rather than replay | No claim of mathematical exactly-once transport; the guarantee is at-most-one automatic external effect |
21
+
22
+ ## AgentSpine contracts derived from the study
23
+
24
+ 1. `agentspine.channel-event/v1` is immutable and binds provider, tenant, account, chat, thread, sender, agent, project, group, privacy, reply target, and observed time.
25
+ 2. The optional worker owns typed inbound, run, lifecycle, health, scheduler, and delivery transitions in external private state.
26
+ 3. Per-agent lanes serialize work; another agent, tenant, profile, project, group, chat, or thread cannot claim the lease.
27
+ 4. Reconciliation expires abandoned leases, detects ambiguous delivery, re-enqueues only demonstrably effect-free failures, and preserves terminal receipts.
28
+ 5. Gateway, adapter, scheduler, queue, worker lease, and host runtime have separate health fields. A live process alone is not healthy.
29
+ 6. Goals require an authenticated local owner assignment and contain one success criterion, next safe step, deadline, checkpoint, heartbeat, blocker, and history.
30
+ 7. Memory, Markdown, persona, relationship, task text, prior output, and MCP never satisfy identity, execution, route, or send policy.
31
+
32
+ ## Regression classes
33
+
34
+ The local tests cover the public failure classes as contracts rather than copying their implementations:
35
+
36
+ - A scheduled result that is delivered but never checkpointed cannot become terminal; run completion persists before delivery preparation.
37
+ - A stopped scheduler cannot hide behind a running gateway because health gates are independent and audited.
38
+ - Restart reconciliation converts an in-flight ambiguous send to `delivery-unknown`; it never floods Telegram by replaying it.
39
+ - Every run has a bounded host timeout and one of the terminal or retryable queue states; no final-tool-call ghost remains leased forever.
40
+ - Lease expiry and startup reconciliation close ghost lane rows and allow only safe work to resume.
41
+ - Duplicate provider updates, parallel workers, and repeated delivery calls converge on one event, one lane, one outbox key, and at most one automatic external send.
42
+
43
+ ## Stronger boundaries retained
44
+
45
+ AgentSpine keeps no global full transcript, exposes no channel, goal, wake, worker, kill-switch, secret, or send administration through MCP, and does not broaden tools from persona or team membership. Host and local policy stores remain physically separate from context state. Explicit host trust and local owner confirmation remain unavoidable security decisions.
@@ -0,0 +1,129 @@
1
+ # Host integration
2
+
3
+ AgentSpine uses native plugin surfaces instead of asking users to paste a large system prompt into every project.
4
+
5
+ ## Claude Code
6
+
7
+ | Component | Path | Purpose |
8
+ |---|---|---|
9
+ | Manifest | `.claude-plugin/plugin.json` | Package identity and version |
10
+ | Marketplace | `.claude-plugin/marketplace.json` | GitHub installation and updates |
11
+ | Skill | `skills/agent-spine/SKILL.md` | Context rules and preservation invariants |
12
+ | MCP | `.mcp.json` | Read-only source tools plus external overlay workflows |
13
+ | Hooks | `hooks/hooks.json` | Automatic briefing, attention, protected-source guard, and rights-bound checkpoints |
14
+
15
+ Install from GitHub:
16
+
17
+ ```bash
18
+ claude plugin marketplace add Maykbiletti/AgentSpine
19
+ claude plugin install agent-spine@agent-spine
20
+ ```
21
+
22
+ Use `claude plugin validate .` in a checkout to validate the manifest and marketplace. Claude Code asks the user to approve executable plugin components according to its trust model.
23
+
24
+ The Claude manifest explicitly references `./.mcp.json`. The hook bundle remains at Claude Code's native auto-discovery path `hooks/hooks.json`; it is deliberately not registered a second time through the manifest. Version `0.8.0` replaces the `0.7.0` plugin cache identity. The hook definition contains only portable documented hook fields; `hooks/version.json` carries the separately validated bundle release and lifecycle contract. The repository checks resolve installed-root variables, perform a real MCP `initialize` handshake, validate exactly one native hook command per event, and exercise staged clean install, previous-version cache rejection, upgrade, host-native source resolution, indexed and lazy Claude memory, automatic multilingual briefing, attention, exact job start, tool checkpoint, new-session resume, purge, and uninstall preservation:
25
+
26
+ ```bash
27
+ npm run host:check
28
+ npm run host:install-check
29
+ npm run acceptance
30
+ ```
31
+
32
+ ### Claude MCP troubleshooting
33
+
34
+ If the plugin is listed but `agent-spine` is missing from `/mcp`, update the marketplace cache and reinstall before starting a new session:
35
+
36
+ ```bash
37
+ claude plugin marketplace update agent-spine
38
+ claude plugin uninstall agent-spine@agent-spine
39
+ claude plugin install agent-spine@agent-spine
40
+ claude plugin list
41
+ claude mcp list
42
+ ```
43
+
44
+ Open `/mcp` in the new interactive session and approve or reconnect `agent-spine`. `Pending approval` means discovery succeeded but Claude Code still needs the user's trust decision. A missing entry after reinstall should be diagnosed from `claude plugin validate .`, `npm run host:check`, and Claude Code's plugin diagnostics; AgentSpine does not write to Claude's user configuration or silently approve itself.
45
+
46
+ ## Codex
47
+
48
+ | Component | Path | Purpose |
49
+ |---|---|---|
50
+ | Manifest | `.codex-plugin/plugin.json` | Package identity plus explicit skill and MCP registration |
51
+ | Skill | `skills/agent-spine/SKILL.md` | Context rules and preservation invariants |
52
+ | MCP | Manifest `mcpServers` | Read-only source tools plus external overlay workflows |
53
+ | Hooks | `hooks/hooks.json` | Auto-discovered lifecycle guardrails |
54
+
55
+ Open `/plugins` in Codex CLI after configuring a marketplace that contains AgentSpine, then start a new session. Codex presents a startup review when a new or changed hook definition needs trust; there is no `/hooks` slash command. Codex records trust against the exact hook-definition hash, so an installed, updated, or previously untrusted bundle is skipped until that current definition is reviewed and trusted. This follows the official [Codex hooks trust and plugin discovery contract](https://developers.openai.com/codex/hooks).
56
+
57
+ Codex discovers the hook at the standard `hooks/hooks.json` path. The file deliberately contains only the documented top-level `description` and `hooks` fields; earlier AgentSpine builds added private `version` and `contract` metadata there, which a strict Codex parser could reject before presenting a trust decision. Cache identity remains in `.codex-plugin/plugin.json`, while Codex records hook trust against the current definition hash. Codex sets `CLAUDE_PLUGIN_ROOT` for hook compatibility, so the same hook bundle can execute in both hosts. The MCP registration uses Codex's `PLUGIN_ROOT` expansion.
58
+
59
+ Verify the live host in a newly started Codex CLI session:
60
+
61
+ ```text
62
+ /plugins
63
+ Trust all and continue
64
+ ```
65
+
66
+ `npm run host:check` proves manifest shape, package containment, and a real MCP handshake. `npm run host:install-check` stages the installed bundle and executes its hook entrypoint with native event JSON. Neither command can manufacture Codex's user-controlled trust receipt; only the startup review in the actual host proves that final boundary.
67
+
68
+ ## BLUN King
69
+
70
+ | Component | Path | Purpose |
71
+ |---|---|---|
72
+ | Manifest | `blun.plugin.json` | Native BLUN plugin identity plus skill, MCP, and lifecycle-hook registration |
73
+ | Skill | `skills/agent-spine/SKILL.md` | Context rules and preservation invariants |
74
+ | MCP | Manifest `mcpServers` | Read-only source tools plus external overlay workflows |
75
+ | Hooks | Manifest `hooks` | Automatic briefing, attention, protected-source guard, and checkpoints |
76
+
77
+ Install the local checkout from Fredrik's TUI:
78
+
79
+ ```text
80
+ /plugins install C:\path\to\AgentSpine
81
+ ```
82
+
83
+ BLUN asks the user to trust a third-party plugin before installation because its MCP server and hooks execute local code. Accept that visible install decision, then use `/reload` or `/new`; BLUN has no separate `/hooks` command. The BLUN adapter maps its isolated `BLUN_HOME` to AgentSpine's Codex-compatible `AGENTS.md` source hierarchy, so user state remains under the BLUN app home instead of leaking into `.codex` or a scanned project.
84
+
85
+ ## Direct MCP use
86
+
87
+ Any MCP client that supports stdio can launch:
88
+
89
+ ```json
90
+ {
91
+ "mcpServers": {
92
+ "agent-spine": {
93
+ "command": "agentspine-mcp"
94
+ }
95
+ }
96
+ }
97
+ ```
98
+
99
+ The server implements `initialize`, `ping`, `tools/list`, and `tools/call`. It has no network dependency and exposes no source-file, delegation-policy, signer, trust, or shared-adapter administration tool. Overlay tools write only private AgentSpine context state outside the scanned project. Explicit delegation grants, key generation and rotation, trust changes, adapter connections, publication, HTTPS snapshot export, object upload or pulls, SQLite paths or operations, import review, and destructive sharing operations remain on the local CLI surface. MCP can only read already reviewed `shared_context`; its authentication summary contains no signature or public-key material. `session_briefing` is a read-only aggregator over these already constrained read paths and cannot widen them.
100
+
101
+ Verify either installation against a synthetic or real project without changing its Markdown:
102
+
103
+ ```bash
104
+ agentspine doctor --json
105
+ npm run host:check
106
+ agentspine audit /path/to/project --json
107
+ ```
108
+
109
+ The audit exits non-zero when a required gate fails, making it suitable for installation smoke tests and CI.
110
+
111
+ Use `agentspine doctor --host claude|codex --cwd /active/project --json` or `agentspine source-status --host claude|codex --cwd /active/project --json` to see the checked scope counts and a concrete empty/fail-closed reason. The lifecycle adapter never substitutes the installation directory for the active host hierarchy. Details and official host references are in [host-native source roots](source-roots.md).
112
+
113
+ The provider-neutral lifecycle adapter covers `SessionStart` (including resume and compact starts), `UserPromptSubmit`, `PreToolUse`, `PostToolUse`, `PreCompact`, `PostCompact`, `Stop`, and `SubagentStop`. Start, prompt, and compaction boundaries scan and inject the actual byte-budgeted `session_briefing`; no model-side MCP selection is required. Prompt submission can additionally capture minimal safe learning and direct promise/blocker signals after the separate local continuity opt-in. `PostToolUse` writes an idempotent task heartbeat; `Stop` and `SubagentStop` close that heartbeat without emitting repeated chat text.
114
+
115
+ When an exact locally registered job is waiting, `SessionStart` acquires its lease and injects its real checkpoint automatically. Subsequent tool and stop hooks resolve that job from the native host session; the model does not need to repeat a job envelope. `PreToolUse` first retains the protected-source guard, then rechecks the current execution grant, assignment, scope, capability, lease, and workspace. `PostToolUse` checkpoints exactly one matching result. A new session resumes only after the same checks. Grant and job administration remain local CLI operations and are absent from MCP. No hook creates permissions.
116
+
117
+ Identity and audience come from explicit hook scope fields or the locally configured default direct-person/project scope. Group content requires an exact group ID and never enters automatic learning. Missing scope produces no inferred identity; corrupt state returns a visible `failedClosed` packet and must never be reported as successful recall.
118
+
119
+ Hook stdin is JSON-only and limited to 64 KiB. State transitions use external atomic files and locks. Hook stdout contains only host protocol JSON; diagnostics are bounded to stderr by the host process. Hooks do not expose transport, key, trust, database, network, message, payment, production, delegation, or policy administration.
120
+
121
+ The first executable-component trust approval remains mandatory. AgentSpine cannot approve itself. After approval and the one-time continuity opt-in, no per-session enablement or voluntary tool call is required.
122
+
123
+ ## Optional gateway worker
124
+
125
+ The package also registers exactly one `agentspine-worker` entrypoint. It is separate from MCP and lifecycle hooks. When an owner runs it under a service manager, it synchronizes the configured authenticated persona roster, polls current Telegram bindings, prepares exact Claude/Codex start data, invokes only the absolute executable in `AGENTSPINE_HOST_RUNNER` without a shell, and returns one idempotent reply to the bound origin.
126
+
127
+ The host runner is responsible for starting the selected host with the supplied scope and `agent_spine_channel_event` fields. Codex still refuses the injected context until the current hook hash has been reviewed in `/hooks`; the worker cannot bypass or manufacture that trust. Setup and the stdin/stdout contract are documented in [durable gateway worker](gateway-runtime.md).
128
+
129
+ The visible acceptance runner invokes the same production lifecycle adapter with new synthetic people, separated groups, Swedish and Spanish prompts, restarts, compaction, correction, rollback, purge, current-rights checks, and durable checkpoints. It prints one reproducible receipt per gate and proves `mcpCalls: 0`. See [visible cross-host acceptance](acceptance.md).
@@ -0,0 +1,116 @@
1
+ # HTTPS snapshot transport
2
+
3
+ AgentSpine can carry a signed directory exchange across ordinary HTTPS without depending on a particular cloud, database, or agent vendor. The basic workflow is deliberately static: AgentSpine creates one immutable JSON snapshot, an operator publishes that file through existing infrastructure, and receivers pull it into the normal local quarantine. For direct create-only uploads, use the separate [immutable HTTPS object transport](object-transport.md).
4
+
5
+ ```mermaid
6
+ sequenceDiagram
7
+ participant P as Publisher CLI
8
+ participant F as Signed directory adapter
9
+ participant H as Operator-controlled HTTPS
10
+ participant R as Receiver CLI
11
+ participant Q as Local quarantine
12
+ P->>F: validate signed manifest + every event
13
+ P->>P: create immutable snapshot outside project
14
+ P-->>H: operator publishes JSON file
15
+ R->>H: pinned-DNS HTTPS GET
16
+ H-->>R: bounded JSON, no redirect/compression
17
+ R->>R: validate snapshot digest + all Ed25519 envelopes
18
+ R->>Q: pending imports only
19
+ Q-->>R: second explicit local review still required
20
+ ```
21
+
22
+ The web server never becomes an authority source. A TLS connection protects bytes in transit. The snapshot digest detects truncation or bundle mutation. The existing Ed25519 envelopes authenticate the configured manifest and event keys. The receiving project's trust store decides which keys are recognized. None of those checks approves a claim, grants a permission, or bypasses review.
23
+
24
+ ## Publish a snapshot
25
+
26
+ Snapshots require a signed directory adapter. First create the signer, trust distribution, adapter, and events as described in [shared memory adapters](shared-memory.md). Then export:
27
+
28
+ ```bash
29
+ agentspine share-snapshot-export /srv/agent-memory/team-alpha \
30
+ --root /path/to/publisher-project \
31
+ --out /srv/static/agent-memory/team-alpha-2026-08-28.json \
32
+ --id snapshot:team-alpha-2026-08-28 \
33
+ --confirm-local-share
34
+ ```
35
+
36
+ The output must be outside the scanned project and must not already exist. AgentSpine writes it with owner-only mode on POSIX systems. Updating a feed means exporting a new file or atomically changing a server-side alias outside this static workflow. `share-https-publish` can create immutable remote objects, but AgentSpine still has no remote overwrite, delete, listing, or mutable alias command.
37
+
38
+ Serve the exact file over HTTPS with:
39
+
40
+ - a certificate accepted by the local Node.js trust store;
41
+ - `Content-Type: application/json`;
42
+ - no content compression;
43
+ - no redirect at the configured URL;
44
+ - access logs and retention appropriate for the snapshot's metadata sensitivity.
45
+
46
+ Snapshots contain shared claims, group IDs, timestamps, public signing identities, and signatures. They contain no private learning, source Markdown, evidence text, tasks, delegation policy, credentials, attention state, or relationship profiles. TLS does not encrypt a snapshot stored on the server; secure storage and server access remain deployment responsibilities.
47
+
48
+ ## Pull a public endpoint
49
+
50
+ Trust every expected event signer in the receiving project first, then run:
51
+
52
+ ```bash
53
+ agentspine share-https-pull \
54
+ https://memory.example.org/team-alpha-2026-08-28.json \
55
+ --root /path/to/receiving-project
56
+
57
+ agentspine share-inbox /path/to/receiving-project --status pending --json
58
+ ```
59
+
60
+ The pull validates the entire response before writing local sharing state. All imports remain `pending` and invisible to `shared_context` until the receiving user accepts each item through `share-review --confirmed-by-user`.
61
+
62
+ For an endpoint protected by a bearer token, provide only the environment variable name:
63
+
64
+ ```bash
65
+ export AGENTSPINE_SNAPSHOT_TOKEN='deployment-supplied-value'
66
+ agentspine share-https-pull \
67
+ https://memory.example.org/private/team-alpha.json \
68
+ --token-env AGENTSPINE_SNAPSHOT_TOKEN \
69
+ --root /path/to/receiving-project
70
+ ```
71
+
72
+ Tokens are read at request time. They are not accepted in URLs, command arguments, snapshots, local sharing state, hook context, MCP results, or command output. Shell history, process environment access, and server logs are still operating-system concerns.
73
+
74
+ ## Private networks
75
+
76
+ Public mode rejects loopback, private, link-local, carrier-grade NAT, multicast, reserved, and documentation address ranges. Every DNS answer is checked and the HTTPS connection is pinned to one vetted answer, preventing a second unvalidated lookup from rebinding the request. Mixed public/private answer sets fail closed.
77
+
78
+ Self-hosted private endpoints require both flags:
79
+
80
+ ```bash
81
+ agentspine share-https-pull \
82
+ https://memory.internal.example/team-alpha.json \
83
+ --allow-private-network \
84
+ --confirm-local-share \
85
+ --root /path/to/receiving-project
86
+ ```
87
+
88
+ The confirmation is a local integration attestation, not permission derived from memory or an agent. TLS certificate verification remains enabled for private networks.
89
+
90
+ ## Network safety limits
91
+
92
+ The reference client:
93
+
94
+ - accepts one explicit `https://` JSON resource with no embedded credentials, query, or fragment;
95
+ - performs one `GET`, disables connection pooling, pins validated DNS, and follows no redirects;
96
+ - rejects non-JSON media types and compressed responses;
97
+ - defaults to a 10-second timeout, configurable only from 1 to 30 seconds;
98
+ - caps the response and validated snapshot at 21 MiB and the event set at 2,000;
99
+ - rejects unknown fields, duplicate IDs, collisions, metadata mismatches, invalid bundle digests, unsigned documents, bad signatures, and untrusted keys before quarantine mutation;
100
+ - deletes its temporary materialization after every success or failure;
101
+ - exposes no HTTPS export or pull operation through MCP.
102
+
103
+ The static client intentionally does not execute remote instructions, enumerate endpoints, accept remote redirects, refresh tokens, upload data, or implement a hosted control plane. Writable object, local SQLite, and peer transports are separate modules and preserve the same local validation and review boundary; hosted database adapters remain extension work.
104
+
105
+ ## Snapshot schema
106
+
107
+ `agentspine.https-snapshot/v1` has exact top-level fields:
108
+
109
+ - stable snapshot, scope, and adapter IDs;
110
+ - generation timestamp;
111
+ - one signed `agentspine.directory-adapter/v1` manifest envelope;
112
+ - up to 2,000 signed `agentspine.shared-event/v1` event envelopes;
113
+ - `authority: context-only`;
114
+ - canonical SHA-256 digest over the complete snapshot body.
115
+
116
+ Snapshot integrity is packaging evidence, not source identity. Manifest and event signatures are verified independently, and every event signer needs explicit local trust.