handoff-relay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +434 -0
- package/dist/api/server.d.ts +16 -0
- package/dist/api/server.js +2578 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +5502 -0
- package/dist/client-CjWEJ02H.d.ts +144 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.js +2623 -0
- package/dist/mcp/server.d.ts +84 -0
- package/dist/mcp/server.js +2950 -0
- package/dist/relay-service-BjF5HNvN.d.ts +712 -0
- package/docs/advanced-manual-setup.md +196 -0
- package/docs/claude-code-setup.md +143 -0
- package/docs/codex-setup.md +135 -0
- package/docs/demo-video-script.md +49 -0
- package/docs/generic-mcp-setup.md +163 -0
- package/docs/launch-copy.md +70 -0
- package/docs/local-self-hosting.md +170 -0
- package/docs/packet-schema.md +116 -0
- package/docs/security-privacy.md +90 -0
- package/docs/troubleshooting.md +180 -0
- package/examples/hydration-receipts/reply-hydration.json +14 -0
- package/examples/packets/ask.json +73 -0
- package/examples/packets/reply.json +62 -0
- package/examples/packets/share.json +52 -0
- package/fixtures/clarification.json +7 -0
- package/fixtures/declined-packet.json +6 -0
- package/fixtures/normal-ask.json +72 -0
- package/fixtures/normal-share.json +51 -0
- package/fixtures/revoked-member.json +7 -0
- package/fixtures/secret-redaction.json +10 -0
- package/fixtures/stale-handoff.json +16 -0
- package/fixtures/superseded-packet.json +6 -0
- package/package.json +103 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Launch Copy
|
|
2
|
+
|
|
3
|
+
## Short X Post
|
|
4
|
+
|
|
5
|
+
Public beta: Handoff, human-approved context handoffs between coding agents.
|
|
6
|
+
|
|
7
|
+
Package selected agent-session context, review before send, review before hydration, keep claims separate from evidence, and get audit receipts for every step.
|
|
8
|
+
|
|
9
|
+
Not shared memory. Not agent Slack. Just safer teammate handoffs.
|
|
10
|
+
|
|
11
|
+
## Preflight Before Posting
|
|
12
|
+
|
|
13
|
+
- Push the release branch to the public GitHub repo.
|
|
14
|
+
- Publish `handoff-relay` to npm.
|
|
15
|
+
- Verify `npx -y handoff-relay doctor --json` runs from a clean temp directory.
|
|
16
|
+
- Record the short demo from `docs/demo-video-script.md` after the npm package is live.
|
|
17
|
+
|
|
18
|
+
## GitHub Release Announcement
|
|
19
|
+
|
|
20
|
+
Handoff is a local-first public beta for human-approved context handoffs between coding agents.
|
|
21
|
+
|
|
22
|
+
The first release focuses on one workflow: a developer can package selected session context into a reviewable packet so another teammate's coding agent can continue without reconstructing the investigation.
|
|
23
|
+
|
|
24
|
+
What is included:
|
|
25
|
+
|
|
26
|
+
- Structured ask, share, reply, and clarification packets.
|
|
27
|
+
- Sender approval before send.
|
|
28
|
+
- Recipient review before hydration.
|
|
29
|
+
- Reply approval before return.
|
|
30
|
+
- Sender-controlled reply hydration.
|
|
31
|
+
- Claims and evidence as separate fields.
|
|
32
|
+
- Redaction that blocks secret-looking content by default.
|
|
33
|
+
- Explicit state machine with invalid transition rejection.
|
|
34
|
+
- SQLite coordination storage.
|
|
35
|
+
- Fastify coordination API.
|
|
36
|
+
- MCP server for Claude Code, Codex, and generic MCP clients.
|
|
37
|
+
- Profile-backed setup with `start`, `invite`, `join`, and `doctor`.
|
|
38
|
+
- CLI support for setup, admin flows, approval tokens, debugging, watch, and demos. Normal handoffs happen through MCP tools inside the coding agent.
|
|
39
|
+
- Audit and hydration receipts.
|
|
40
|
+
- Local polling watcher with terminal output, best-effort desktop notifications, and generic webhook posts.
|
|
41
|
+
- Docs, examples, and a five-minute two-user demo.
|
|
42
|
+
|
|
43
|
+
What this is not:
|
|
44
|
+
|
|
45
|
+
- Not passive agent memory.
|
|
46
|
+
- Not Slack for agents.
|
|
47
|
+
- Not autonomous agent-to-agent chat.
|
|
48
|
+
- Not a hosted SaaS dependency.
|
|
49
|
+
|
|
50
|
+
Try it:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# Sam hosts a reachable workspace
|
|
54
|
+
npx -y handoff-relay start --lan
|
|
55
|
+
npx -y handoff-relay invite alice
|
|
56
|
+
|
|
57
|
+
# Alice joins from her machine
|
|
58
|
+
npx -y handoff-relay join http://<host>:3737/invite/<invite-token>
|
|
59
|
+
npx -y handoff-relay doctor
|
|
60
|
+
|
|
61
|
+
# Then wire Handoff into Codex, Claude Code, Cursor, or another MCP client.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Comparison
|
|
65
|
+
|
|
66
|
+
Compared with raw Slack or paste workflows, Relay keeps handoffs structured, reviewed, evidence-backed, and auditable.
|
|
67
|
+
|
|
68
|
+
Compared with passive memory tools, Relay does not watch every session or ingest everything by default. A human chooses what crosses the boundary.
|
|
69
|
+
|
|
70
|
+
Compared with autonomous agent-to-agent systems, Relay keeps both humans in the loop: sender approves send, recipient approves hydration, recipient approves replies, sender approves reply hydration.
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# Local Self-Hosting
|
|
2
|
+
|
|
3
|
+
Handoff is local-first, but team handoff needs a server Alice's machine can reach. For teammates on the same Wi-Fi, Sam can host directly from his machine:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npx -y handoff-relay start --lan
|
|
7
|
+
npx -y handoff-relay invite alice
|
|
8
|
+
npx -y handoff-relay doctor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Alice runs the printed join command on her own machine:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npx -y handoff-relay join http://<sam-lan-ip>:3737/invite/<invite-token>
|
|
15
|
+
npx -y handoff-relay doctor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Plain `start` is for local demos, CI smoke tests, or two profiles on one machine. It resets the active profile to loopback-only invite links. Re-run `start --lan` or pass `--public-url` before creating new invites that another machine should join.
|
|
19
|
+
|
|
20
|
+
For a dedicated trusted host, run one coordination server and have teammates join an invite from that server.
|
|
21
|
+
|
|
22
|
+
## Coordination Server
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npx -y handoff-relay server start \
|
|
26
|
+
--db /srv/handoff/relay.db \
|
|
27
|
+
--host 10.0.0.10 \
|
|
28
|
+
--port 3737
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Put the host behind your normal network controls. Handoff does not provide a hosted cloud service.
|
|
32
|
+
|
|
33
|
+
For local profile-managed servers started by `handoff start`, inspect or stop the recorded background process:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npx -y handoff-relay server status
|
|
37
|
+
npx -y handoff-relay server stop
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Profiles For Teammates
|
|
41
|
+
|
|
42
|
+
Create a workspace and invite members with the advanced commands, or run `start --lan` on the host machine and use the printed invite command.
|
|
43
|
+
|
|
44
|
+
After a teammate runs:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx -y handoff-relay join http://10.0.0.10:3737/invite/<invite-token>
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
their local profile stores the server URL, member token, workspace ID, and approval secret. They can install one supported local MCP config while joining:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npx -y handoff-relay join http://10.0.0.10:3737/invite/<invite-token> --install-mcp codex
|
|
54
|
+
# or
|
|
55
|
+
npx -y handoff-relay join http://10.0.0.10:3737/invite/<invite-token> --install-mcp cursor
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Their MCP command stays profile-backed:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npx -y handoff-relay server mcp --profile default
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Explicit Server-Backed Commands
|
|
65
|
+
|
|
66
|
+
Low-level server-backed commands remain available for automation:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npx -y handoff-relay inbox \
|
|
70
|
+
--server-url http://10.0.0.10:3737 \
|
|
71
|
+
--token <token> \
|
|
72
|
+
--workspace <workspace-id>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Configure project/repo aliases once per workspace so clone names and local repo aliases resolve to the same packet history:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx -y handoff-relay workspace alias set \
|
|
79
|
+
--server-url http://10.0.0.10:3737 \
|
|
80
|
+
--token <admin-token> \
|
|
81
|
+
--workspace <workspace-id> \
|
|
82
|
+
--canonical handoff \
|
|
83
|
+
--alias relay-local \
|
|
84
|
+
--json
|
|
85
|
+
|
|
86
|
+
npx -y handoff-relay workspace alias list \
|
|
87
|
+
--server-url http://10.0.0.10:3737 \
|
|
88
|
+
--token <token> \
|
|
89
|
+
--workspace <workspace-id> \
|
|
90
|
+
--json
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Approval Tokens
|
|
94
|
+
|
|
95
|
+
Strict profile-backed approval tokens use the local profile:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx -y handoff-relay approval-token <packet-id> --action send
|
|
99
|
+
npx -y handoff-relay approval-token <packet-id> --action hydrate
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Profile-backed MCP can opt into agent-confirmed approvals:
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
npx -y handoff-relay server mcp --profile default --agent-approvals
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
In that mode, the MCP process requests and consumes the same short-lived approval tokens through the configured Handoff backend after the agent shows the packet and you explicitly tell it to send, approve, or hydrate. Local database profiles keep that request local; remote/self-hosted profiles send the approval secret to the configured Handoff server API.
|
|
109
|
+
|
|
110
|
+
Explicit approval-token mode remains available:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
npx -y handoff-relay approval-token <packet-id> \
|
|
114
|
+
--server-url http://10.0.0.10:3737 \
|
|
115
|
+
--token <token> \
|
|
116
|
+
--approval-secret <approval-secret> \
|
|
117
|
+
--action send
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Approval secrets stay outside MCP config. `HANDOFF_APPROVAL_SECRET` and the older `AGENT_RELAY_APPROVAL_SECRET` alias are supported for the terminal running approval commands.
|
|
121
|
+
|
|
122
|
+
## SQLite Operations
|
|
123
|
+
|
|
124
|
+
- The coordination server database path is controlled with `--db`, `HANDOFF_DB`, or `AGENT_RELAY_DB`.
|
|
125
|
+
- Back up the main `.db` file plus WAL/SHM files when WAL mode is active.
|
|
126
|
+
- Treat the database as sensitive: packet bodies and token hashes live there.
|
|
127
|
+
- Do not put `.relay/*.db` in git.
|
|
128
|
+
|
|
129
|
+
## Watch Mode
|
|
130
|
+
|
|
131
|
+
Terminal polling watcher:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
npx -y handoff-relay watch \
|
|
135
|
+
--server-url http://10.0.0.10:3737 \
|
|
136
|
+
--token <token> \
|
|
137
|
+
--workspace <workspace-id> \
|
|
138
|
+
--interval 5000
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Add best-effort native desktop notifications on the machine running the watcher:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
npx -y handoff-relay watch \
|
|
145
|
+
--server-url http://10.0.0.10:3737 \
|
|
146
|
+
--token <token> \
|
|
147
|
+
--workspace <workspace-id> \
|
|
148
|
+
--desktop-notifications
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Post concise notification summaries to a generic webhook endpoint:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx -y handoff-relay watch \
|
|
155
|
+
--server-url http://10.0.0.10:3737 \
|
|
156
|
+
--token <token> \
|
|
157
|
+
--workspace <workspace-id> \
|
|
158
|
+
--webhook-url https://hooks.example.test/relay \
|
|
159
|
+
--webhook-header "Authorization: Bearer <token>"
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The watcher always uses polling. Terminal, desktop, and webhook notifications include sender handle, packet type, title, project, summary, and the open/review action, but never evidence bodies or raw transcripts. For scripts or tests, use `--once` to poll a single time and exit.
|
|
163
|
+
|
|
164
|
+
## From A Local Checkout
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
pnpm install
|
|
168
|
+
pnpm build
|
|
169
|
+
node dist/cli.js server start --db /srv/handoff/relay.db --host 10.0.0.10 --port 3737
|
|
170
|
+
```
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Packet Schema
|
|
2
|
+
|
|
3
|
+
Relay packets are Zod-validated TypeScript objects. The source lives in `src/protocol/schema.ts`.
|
|
4
|
+
|
|
5
|
+
## Packet Types
|
|
6
|
+
|
|
7
|
+
- `ask`: requires `question`.
|
|
8
|
+
- `share`: requires `finding`.
|
|
9
|
+
- `reply`: requires `answer`.
|
|
10
|
+
- `clarification`: requires `question` and references an original packet.
|
|
11
|
+
|
|
12
|
+
## Required Contract Fields
|
|
13
|
+
|
|
14
|
+
- `packet_id`
|
|
15
|
+
- `packet_type`
|
|
16
|
+
- `workspace_id`
|
|
17
|
+
- `sender_member_id`
|
|
18
|
+
- `recipient_member_ids`
|
|
19
|
+
- `created_at`
|
|
20
|
+
- `updated_at`
|
|
21
|
+
- `expires_at` or `recheck_by`
|
|
22
|
+
- `status`
|
|
23
|
+
- `project`
|
|
24
|
+
- `source_client`
|
|
25
|
+
- `title`
|
|
26
|
+
- `summary`
|
|
27
|
+
- `claims`
|
|
28
|
+
- `evidence`
|
|
29
|
+
- `files_or_symbols`
|
|
30
|
+
- `commands_or_tests_run`
|
|
31
|
+
- `what_was_tried`
|
|
32
|
+
- `known_failures`
|
|
33
|
+
- `current_hypothesis`
|
|
34
|
+
- `confidence`
|
|
35
|
+
- `suggested_next_steps`
|
|
36
|
+
- `redaction_report`
|
|
37
|
+
- `hydration_policy`
|
|
38
|
+
- `audit_receipt`
|
|
39
|
+
|
|
40
|
+
## Project Identity
|
|
41
|
+
|
|
42
|
+
Packet `project.repo_name` is the canonical workspace project name when a matching project/repo alias is configured. Configure aliases with `workspace alias set` or `relay_configure_project_alias`; history/search filters accept either the canonical project or a configured alias.
|
|
43
|
+
|
|
44
|
+
```json
|
|
45
|
+
{
|
|
46
|
+
"repo_name": "handoff",
|
|
47
|
+
"git_remote_fingerprint": "sha256:...",
|
|
48
|
+
"branch": "main",
|
|
49
|
+
"commit_hash": "abc123"
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Claims
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"claim_id": "clm_1",
|
|
58
|
+
"text": "The failure happens after refresh token rotation.",
|
|
59
|
+
"confidence": "medium",
|
|
60
|
+
"status": "observed",
|
|
61
|
+
"evidence_ids": ["ev_1"],
|
|
62
|
+
"needs_recheck": true
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Evidence
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"evidence_id": "ev_1",
|
|
71
|
+
"kind": "test_failure",
|
|
72
|
+
"label": "vitest failure",
|
|
73
|
+
"source": "pnpm test auth-refresh",
|
|
74
|
+
"excerpt": "expected 200, received 401",
|
|
75
|
+
"hash": "sha256:...",
|
|
76
|
+
"captured_at": "2026-06-15T12:00:00.000Z",
|
|
77
|
+
"sensitivity": "normal"
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## State Model
|
|
82
|
+
|
|
83
|
+
Supported statuses:
|
|
84
|
+
|
|
85
|
+
```text
|
|
86
|
+
draft
|
|
87
|
+
pending_sender_approval
|
|
88
|
+
sent
|
|
89
|
+
delivered
|
|
90
|
+
viewed
|
|
91
|
+
accepted
|
|
92
|
+
clarification_requested
|
|
93
|
+
response_drafting
|
|
94
|
+
pending_recipient_approval
|
|
95
|
+
replied
|
|
96
|
+
hydrated
|
|
97
|
+
archived
|
|
98
|
+
declined
|
|
99
|
+
expired
|
|
100
|
+
superseded
|
|
101
|
+
closed_resolved
|
|
102
|
+
closed_unresolved
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Invalid transitions throw `INVALID_STATE_TRANSITION`.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
|
|
109
|
+
- `pending_sender_approval` drafts can be edited with `update-draft`/`relay_update_draft`; edits recalculate redaction and write an `edit` audit receipt.
|
|
110
|
+
- `pending_sender_approval -> sent` only by sender.
|
|
111
|
+
- `sent -> delivered` only by system.
|
|
112
|
+
- `delivered -> viewed -> accepted -> hydrated` only by recipient.
|
|
113
|
+
- `response_drafting -> pending_recipient_approval -> replied` only by recipient.
|
|
114
|
+
- `replied -> viewed -> hydrated` by the reply recipient.
|
|
115
|
+
|
|
116
|
+
See [example packets](../examples/packets/).
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Security And Privacy Model
|
|
2
|
+
|
|
3
|
+
Handoff is built around review gates, bounded packets, and local ownership.
|
|
4
|
+
|
|
5
|
+
## What Crosses The Boundary
|
|
6
|
+
|
|
7
|
+
Packets carry selected fields:
|
|
8
|
+
|
|
9
|
+
- Summary, question/finding/answer.
|
|
10
|
+
- Claims with confidence, status, evidence ids, and recheck flags.
|
|
11
|
+
- Evidence with kind, source, excerpt, hash, capture time, and sensitivity.
|
|
12
|
+
- Project identity: repo name, remote fingerprint, branch, optional commit.
|
|
13
|
+
- Audit and redaction receipts.
|
|
14
|
+
|
|
15
|
+
Raw transcripts are not captured or shared by default.
|
|
16
|
+
|
|
17
|
+
## Required Human Gates
|
|
18
|
+
|
|
19
|
+
- Sender reviews and approves ask/share packets before send.
|
|
20
|
+
- Recipient views and accepts ask/share packets before hydration.
|
|
21
|
+
- Recipient reviews and approves replies before return.
|
|
22
|
+
- Sender views replies and hydrates them explicitly.
|
|
23
|
+
|
|
24
|
+
The service rejects invalid state transitions, and send/reply/hydrate actions require a short-lived human approval token. Strict mode generates that token outside MCP through the local CLI confirmation prompt. Agent-confirmed mode is an opt-in profile-backed MCP mode where the MCP process can request the same short-lived token through the configured Handoff backend after the agent shows the packet and the user explicitly tells it to send, approve, or hydrate. Local database profiles keep that request local; remote profiles send the approval secret to the configured Handoff server API.
|
|
25
|
+
|
|
26
|
+
Approval-token minting requires both the member token and a separate per-member approval secret returned during setup/acceptance. A member token alone cannot mint approval tokens. Agent-confirmed mode does not cryptographically prove a fresh terminal phrase; it treats explicit instruction in the active local agent session as the approval event.
|
|
27
|
+
|
|
28
|
+
## Redaction
|
|
29
|
+
|
|
30
|
+
The redaction engine blocks by default when it sees:
|
|
31
|
+
|
|
32
|
+
- API-key or token-looking values.
|
|
33
|
+
- Private key blocks.
|
|
34
|
+
- Credential-bearing URLs.
|
|
35
|
+
- `.env`-like secret values.
|
|
36
|
+
- Evidence marked `secret_detected` or `restricted`.
|
|
37
|
+
|
|
38
|
+
Redaction scans packet titles, summaries, questions/findings/answers, hypotheses, claims, evidence labels/sources/excerpts, files or symbols, commands/tests, tried steps, known failures, and suggested next steps before a packet can be approved.
|
|
39
|
+
|
|
40
|
+
It warns, but does not block by default, for:
|
|
41
|
+
|
|
42
|
+
- Local absolute paths.
|
|
43
|
+
- Oversized excerpts that should be compressed.
|
|
44
|
+
|
|
45
|
+
Hashes are preserved when evidence excerpts are truncated or omitted.
|
|
46
|
+
|
|
47
|
+
## Authorization
|
|
48
|
+
|
|
49
|
+
Authorization is enforced in the service/storage layer:
|
|
50
|
+
|
|
51
|
+
- Members can send only inside their workspace.
|
|
52
|
+
- Members can read packets they sent or packets addressed to them.
|
|
53
|
+
- Admins can manage workspace membership and view audit metadata by default.
|
|
54
|
+
- Admin packet body access is off by default and must be enabled as a workspace setting.
|
|
55
|
+
- Audit receipts are available through CLI, API, and MCP. Workspace-wide audit listing is admin-only; packet-specific audit listing follows metadata visibility.
|
|
56
|
+
- Revoked members cannot authenticate and cannot receive future packets.
|
|
57
|
+
- Search and history filter before returning results. Admins without body access can search and filter metadata fields only; body/provenance text is not exposed as a search oracle.
|
|
58
|
+
|
|
59
|
+
## Audit Receipts
|
|
60
|
+
|
|
61
|
+
Receipts are recorded for:
|
|
62
|
+
|
|
63
|
+
- `draft`
|
|
64
|
+
- `approve`
|
|
65
|
+
- `send`
|
|
66
|
+
- `deliver`
|
|
67
|
+
- `view`
|
|
68
|
+
- `accept`
|
|
69
|
+
- `edit`
|
|
70
|
+
- `hydrate`
|
|
71
|
+
- `reply`
|
|
72
|
+
- `decline`
|
|
73
|
+
- `archive`
|
|
74
|
+
- `close`
|
|
75
|
+
- `configure_project_alias`
|
|
76
|
+
- `revoke`
|
|
77
|
+
- `rotate_token`
|
|
78
|
+
- `rotate_approval_secret`
|
|
79
|
+
- `search`
|
|
80
|
+
|
|
81
|
+
Receipts avoid logging extra raw sensitive evidence beyond the approved packet body.
|
|
82
|
+
|
|
83
|
+
## Operational Notes
|
|
84
|
+
|
|
85
|
+
- Treat `.relay/*.db` as sensitive. It contains packet bodies, member token hashes, audit metadata, and hydration receipts.
|
|
86
|
+
- Treat approval secrets like local signing material. Keep them out of MCP config, tool schemas, and committed files. Strict mode uses them through `approval-token`; agent-confirmed mode lets the local profile-backed MCP process load them without exposing them to the agent as arguments.
|
|
87
|
+
- Rotate approval secrets with `member rotate-approval-secret` if one appears in shell history, terminal logs, or copied demo output. Rotation requires the current approval secret, returns the replacement once, invalidates unconsumed approval tokens minted by that member, and records `rotate_approval_secret`.
|
|
88
|
+
- Back up the SQLite database like any local coordination data store.
|
|
89
|
+
- Prefer private network access or localhost-only binding for the Fastify coordination server.
|
|
90
|
+
- Rotate member tokens if a token is pasted into a prompt, shell history, ticket, or chat. Rotate both credentials if both the member token and approval secret are exposed.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
## Start With Doctor
|
|
4
|
+
|
|
5
|
+
Run:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx -y handoff-relay doctor
|
|
9
|
+
npx -y handoff-relay doctor --json
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Doctor checks the Handoff home directory, active profile, credential file permissions, member token, approval secret, server reachability, workspace access, and the profile-backed MCP command.
|
|
13
|
+
|
|
14
|
+
If the profile is missing and you are hosting a workspace for teammates, run:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx -y handoff-relay start --lan
|
|
18
|
+
npx -y handoff-relay invite alice
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If you are joining someone else's workspace, ask them for the invite command and run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx -y handoff-relay join <invite-link>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Plain `start` is only the right recovery path for local demos, CI smoke tests, or two profiles on one machine.
|
|
28
|
+
|
|
29
|
+
If `doctor` reports `WARN` for `mcp_config`, add Handoff to your MCP client. For Codex or Cursor, Handoff can write the common global config while hosting or joining:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx -y handoff-relay start --lan --install-mcp codex
|
|
33
|
+
npx -y handoff-relay start --lan --install-mcp cursor
|
|
34
|
+
npx -y handoff-relay join <invite-link> --install-mcp codex
|
|
35
|
+
npx -y handoff-relay join <invite-link> --install-mcp cursor
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For Claude Code or another MCP client, add the printed profile-backed command:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx -y handoff-relay server mcp --profile default
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
If a teammate cannot join from another machine, restart the host in LAN mode and send a fresh invite:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx -y handoff-relay start --lan
|
|
48
|
+
npx -y handoff-relay invite alice
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## `better-sqlite3` Cannot Find Native Bindings
|
|
52
|
+
|
|
53
|
+
Run:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm rebuild better-sqlite3 esbuild
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This package includes `pnpm-workspace.yaml` with `onlyBuiltDependencies` for `better-sqlite3` and `esbuild`. If your pnpm policy still blocks builds, approve those packages for this checkout.
|
|
60
|
+
|
|
61
|
+
## MCP Server Starts But Tools Do Not Appear
|
|
62
|
+
|
|
63
|
+
- Confirm `pnpm build` produced `dist/cli.js`.
|
|
64
|
+
- Run the command directly:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
npx -y handoff-relay server mcp --profile default
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- In Claude Code, check `/mcp` or start with `--mcp-config`.
|
|
71
|
+
- In Codex, confirm the trusted `config.toml` has the expected `mcp_servers.<id>.command` and `args`.
|
|
72
|
+
- Confirm the MCP args use `--profile default` unless you intentionally need `--explicit-auth`.
|
|
73
|
+
|
|
74
|
+
## Redaction Blocks A Packet
|
|
75
|
+
|
|
76
|
+
Handoff blocks secret-looking content by default. Remove the evidence excerpt, replace it with a hash or summary, then create a new draft.
|
|
77
|
+
|
|
78
|
+
Common causes:
|
|
79
|
+
|
|
80
|
+
- `API_KEY=...`
|
|
81
|
+
- `TOKEN=...`
|
|
82
|
+
- Private key PEM blocks.
|
|
83
|
+
- `postgres://user:pass@host/db`
|
|
84
|
+
- Evidence marked `secret_detected` or `restricted`.
|
|
85
|
+
|
|
86
|
+
## Recipient Cannot See A Packet
|
|
87
|
+
|
|
88
|
+
Check:
|
|
89
|
+
|
|
90
|
+
- The recipient handle is in the same workspace.
|
|
91
|
+
- The member is active, not revoked.
|
|
92
|
+
- The packet was approved and delivered.
|
|
93
|
+
- The token belongs to the intended workspace.
|
|
94
|
+
|
|
95
|
+
Use:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx -y handoff-relay member list --db .relay/team.db --token <admin-token> --workspace <workspace-id>
|
|
99
|
+
npx -y handoff-relay status <packet-id> --db .relay/team.db --token <sender-token>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Hydration Or Approval Is Rejected
|
|
103
|
+
|
|
104
|
+
Strict mode requires explicit review and a human approval token:
|
|
105
|
+
|
|
106
|
+
- Ask/share packets: recipient must `view`, then `accept`, then generate an `approval-token --approval-secret <secret> --action hydrate`, then `hydrate`.
|
|
107
|
+
- Reply packets: sender must `view`, then generate an `approval-token --approval-secret <secret> --action hydrate`, then `hydrate`.
|
|
108
|
+
|
|
109
|
+
Invalid transitions return `INVALID_STATE_TRANSITION`.
|
|
110
|
+
|
|
111
|
+
Missing, expired, reused, or wrong-action approval tokens return `FORBIDDEN`.
|
|
112
|
+
|
|
113
|
+
If you intended to use agent-confirmed approvals, confirm your MCP command includes profile mode and `--agent-approvals`:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
npx -y handoff-relay server mcp --profile default --agent-approvals
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Agent-confirmed approvals are not available in explicit-auth MCP mode or when the local profile credentials are missing.
|
|
120
|
+
|
|
121
|
+
If `/packets/:packetId/approval-token` returns `FORBIDDEN` with an approval secret message, use the CLI `approval-token` command with the approval secret returned during setup/acceptance. Static local-renderer headers are ignored.
|
|
122
|
+
|
|
123
|
+
In profile-backed mode:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
npx -y handoff-relay approval-token <packet-id> --action send
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
In explicit advanced mode:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npx -y handoff-relay approval-token <packet-id> --db .relay/team.db --token <member-token> --approval-secret <approval-secret> --action send
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For audit/debugging:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
npx -y handoff-relay history --db .relay/team.db --token <member-token> --workspace <workspace-id> --filter open
|
|
139
|
+
npx -y handoff-relay audit --db .relay/team.db --token <admin-token> --workspace <workspace-id>
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
If an approval secret leaks, rotate it and update your local secret manager:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx -y handoff-relay member rotate-approval-secret --db .relay/team.db --token <member-token> --approval-secret <current-approval-secret> --json
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The old approval secret stops working immediately, and unconsumed approval tokens already minted by that member are invalidated. If you no longer have the current approval secret, ask a workspace admin to revoke and reinvite the member.
|
|
149
|
+
|
|
150
|
+
## Packet Needs More Evidence
|
|
151
|
+
|
|
152
|
+
Recipients can ask for clarification before hydration:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npx -y handoff-relay clarify <packet-id> --db .relay/team.db --token <recipient-token> --question "Can you include the failing assertion?" --requested-evidence "test failure"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Watcher Prints Nothing
|
|
159
|
+
|
|
160
|
+
The watcher only reports new packet ids once per process. Check `inbox` directly:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npx -y handoff-relay inbox --db .relay/team.db --token <token> --workspace <workspace-id>
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Desktop Or Webhook Notifications Do Not Fire
|
|
167
|
+
|
|
168
|
+
Desktop notifications are best-effort from the local watcher process:
|
|
169
|
+
|
|
170
|
+
- macOS uses `osascript`.
|
|
171
|
+
- Linux uses `notify-send`; install your desktop notification package if it is missing.
|
|
172
|
+
- Windows uses a PowerShell tray notification.
|
|
173
|
+
|
|
174
|
+
Webhook notifications require a reachable endpoint that accepts JSON POSTs:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
npx -y handoff-relay watch --db .relay/team.db --token <token> --workspace <workspace-id> --webhook-url https://hooks.example.test/relay --once
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
If the adapter fails, `watch` still prints the terminal notification and writes an adapter warning to stderr.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"receipt_id": "rcp_hydrate_example",
|
|
3
|
+
"action": "hydrate",
|
|
4
|
+
"actor_member_id": "mem_sam",
|
|
5
|
+
"packet_id": "pkt_example_reply",
|
|
6
|
+
"workspace_id": "wrk_example",
|
|
7
|
+
"created_at": "2026-06-15T12:25:00.000Z",
|
|
8
|
+
"metadata": {
|
|
9
|
+
"client": "codex",
|
|
10
|
+
"session_id": "sam-session",
|
|
11
|
+
"packet_type": "reply"
|
|
12
|
+
},
|
|
13
|
+
"receipt_hash": "sha256:example"
|
|
14
|
+
}
|