@poa-box/agent 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/.env.agent.template +20 -0
- package/README.md +46 -0
- package/brain/Config/agent-config.json +14 -0
- package/brain/Config/brain-allowlist.json +20 -0
- package/brain/Identity/goals.template.md +23 -0
- package/brain/Identity/how-i-think.md +406 -0
- package/brain/Identity/who-i-am.template.md +34 -0
- package/brain/Knowledge/BOOTSTRAP.md +66 -0
- package/brain/Knowledge/audit-corpus-index.json +406 -0
- package/brain/Knowledge/discussions.json +245 -0
- package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
- package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
- package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
- package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
- package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
- package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
- package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
- package/brain/Knowledge/projects.md +181 -0
- package/brain/Knowledge/risk-framework.md +90 -0
- package/brain/Knowledge/shared.md +416 -0
- package/brain/Knowledge/sprint-priorities.md +439 -0
- package/brain/Memory/.gitkeep +0 -0
- package/dist/commands/agent/daily-digest.d.ts +24 -0
- package/dist/commands/agent/daily-digest.js +336 -0
- package/dist/commands/agent/delegate.d.ts +12 -0
- package/dist/commands/agent/delegate.js +91 -0
- package/dist/commands/agent/deploy-to-org.d.ts +20 -0
- package/dist/commands/agent/deploy-to-org.js +154 -0
- package/dist/commands/agent/index.d.ts +2 -0
- package/dist/commands/agent/index.js +27 -0
- package/dist/commands/agent/init.d.ts +19 -0
- package/dist/commands/agent/init.js +303 -0
- package/dist/commands/agent/onboard.d.ts +22 -0
- package/dist/commands/agent/onboard.js +192 -0
- package/dist/commands/agent/paymaster-status.d.ts +14 -0
- package/dist/commands/agent/paymaster-status.js +130 -0
- package/dist/commands/agent/register.d.ts +21 -0
- package/dist/commands/agent/register.js +116 -0
- package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
- package/dist/commands/agent/setup-sponsorship.js +154 -0
- package/dist/commands/agent/status.d.ts +12 -0
- package/dist/commands/agent/status.js +171 -0
- package/dist/commands/agent/triage.d.ts +12 -0
- package/dist/commands/agent/triage.js +503 -0
- package/dist/commands/brain/advance-stage.d.ts +42 -0
- package/dist/commands/brain/advance-stage.js +206 -0
- package/dist/commands/brain/allowlist.d.ts +30 -0
- package/dist/commands/brain/allowlist.js +274 -0
- package/dist/commands/brain/append-lesson.d.ts +55 -0
- package/dist/commands/brain/append-lesson.js +245 -0
- package/dist/commands/brain/brainstorm.d.ts +154 -0
- package/dist/commands/brain/brainstorm.js +573 -0
- package/dist/commands/brain/daemon.d.ts +31 -0
- package/dist/commands/brain/daemon.js +348 -0
- package/dist/commands/brain/doctor.d.ts +27 -0
- package/dist/commands/brain/doctor.js +497 -0
- package/dist/commands/brain/edit-lesson.d.ts +51 -0
- package/dist/commands/brain/edit-lesson.js +248 -0
- package/dist/commands/brain/import-snapshot.d.ts +68 -0
- package/dist/commands/brain/import-snapshot.js +177 -0
- package/dist/commands/brain/index.d.ts +2 -0
- package/dist/commands/brain/index.js +67 -0
- package/dist/commands/brain/list.d.ts +21 -0
- package/dist/commands/brain/list.js +83 -0
- package/dist/commands/brain/migrate-projects.d.ts +44 -0
- package/dist/commands/brain/migrate-projects.js +209 -0
- package/dist/commands/brain/migrate.d.ts +74 -0
- package/dist/commands/brain/migrate.js +306 -0
- package/dist/commands/brain/new-project.d.ts +53 -0
- package/dist/commands/brain/new-project.js +226 -0
- package/dist/commands/brain/read.d.ts +24 -0
- package/dist/commands/brain/read.js +81 -0
- package/dist/commands/brain/remove-lesson.d.ts +47 -0
- package/dist/commands/brain/remove-lesson.js +206 -0
- package/dist/commands/brain/remove-project.d.ts +36 -0
- package/dist/commands/brain/remove-project.js +177 -0
- package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
- package/dist/commands/brain/retro-file-tasks.js +372 -0
- package/dist/commands/brain/retro-list.d.ts +28 -0
- package/dist/commands/brain/retro-list.js +125 -0
- package/dist/commands/brain/retro-mark-change.d.ts +58 -0
- package/dist/commands/brain/retro-mark-change.js +176 -0
- package/dist/commands/brain/retro-remove.d.ts +36 -0
- package/dist/commands/brain/retro-remove.js +142 -0
- package/dist/commands/brain/retro-respond.d.ts +56 -0
- package/dist/commands/brain/retro-respond.js +250 -0
- package/dist/commands/brain/retro-show.d.ts +23 -0
- package/dist/commands/brain/retro-show.js +100 -0
- package/dist/commands/brain/retro-start.d.ts +55 -0
- package/dist/commands/brain/retro-start.js +311 -0
- package/dist/commands/brain/search.d.ts +48 -0
- package/dist/commands/brain/search.js +190 -0
- package/dist/commands/brain/snapshot.d.ts +32 -0
- package/dist/commands/brain/snapshot.js +243 -0
- package/dist/commands/brain/status.d.ts +15 -0
- package/dist/commands/brain/status.js +166 -0
- package/dist/commands/brain/subscribe.d.ts +28 -0
- package/dist/commands/brain/subscribe.js +90 -0
- package/dist/commands/brain/tag.d.ts +46 -0
- package/dist/commands/brain/tag.js +192 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +22 -0
- package/dist/lib/brain-daemon.d.ts +126 -0
- package/dist/lib/brain-daemon.js +811 -0
- package/dist/lib/brain-membership.d.ts +58 -0
- package/dist/lib/brain-membership.js +115 -0
- package/dist/lib/brain-migrate-projects.d.ts +43 -0
- package/dist/lib/brain-migrate-projects.js +247 -0
- package/dist/lib/brain-migrate.d.ts +77 -0
- package/dist/lib/brain-migrate.js +328 -0
- package/dist/lib/brain-ops.d.ts +271 -0
- package/dist/lib/brain-ops.js +571 -0
- package/dist/lib/brain-paths.d.ts +15 -0
- package/dist/lib/brain-paths.js +33 -0
- package/dist/lib/brain-projections.d.ts +216 -0
- package/dist/lib/brain-projections.js +829 -0
- package/dist/lib/brain-schemas.d.ts +36 -0
- package/dist/lib/brain-schemas.js +316 -0
- package/dist/lib/brain-signing.d.ts +103 -0
- package/dist/lib/brain-signing.js +256 -0
- package/dist/lib/brain.d.ts +198 -0
- package/dist/lib/brain.js +1057 -0
- package/dist/pop-agent.d.ts +1 -0
- package/dist/pop-agent.js +18 -0
- package/docs/agent.md +126 -0
- package/docs/agents/brain-anti-entropy.md +127 -0
- package/docs/agents/brain-cross-device-onboarding.md +210 -0
- package/docs/agents/brain-cross-machine-smoke.md +241 -0
- package/docs/agents/brain-layer-setup.md +725 -0
- package/docs/agents/offboarding-protocol.md +188 -0
- package/docs/agents/onboarding-protocol.md +243 -0
- package/docs/agents/running-an-agent.md +200 -0
- package/docs/brain.md +560 -0
- package/package.json +61 -0
- package/scripts/apply.sh +140 -0
- package/scripts/onboard.sh +205 -0
- package/scripts/setup-agent.ts +272 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* pop-agent — the agent-runtime entry point.
|
|
5
|
+
*
|
|
6
|
+
* Identical to `pop` except the agent + brain command groups are visible in
|
|
7
|
+
* help. The human `pop` binary hides them (they still execute when invoked
|
|
8
|
+
* explicitly, so skills and muscle memory keep working); this bin is what an
|
|
9
|
+
* agent process or its operator should run.
|
|
10
|
+
*
|
|
11
|
+
* Implementation: set the mode flag, then hand over to @poa-box/cli, whose entry
|
|
12
|
+
* module runs the CLI on load. The agent/brain commands themselves are
|
|
13
|
+
* registered by @poa-box/cli's plugin probe finding this package — see
|
|
14
|
+
* `registerAgentSurface` in src/index.ts (this package).
|
|
15
|
+
*/
|
|
16
|
+
process.env.POP_AGENT_MODE = '1';
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
18
|
+
require('@poa-box/cli');
|
package/docs/agent.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!-- AUTO-GENERATED by scripts/generate-cli-docs.ts — do not edit. Regenerate: yarn docs:gen -->
|
|
2
|
+
|
|
3
|
+
# pop agent
|
|
4
|
+
|
|
5
|
+
Agent operations & monitoring
|
|
6
|
+
|
|
7
|
+
## pop agent status
|
|
8
|
+
|
|
9
|
+
Show agent operational status and action items
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
pop agent status [flags]
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## pop agent triage
|
|
16
|
+
|
|
17
|
+
Prioritized action plan for current heartbeat
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
pop agent triage [flags]
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## pop agent daily-digest
|
|
24
|
+
|
|
25
|
+
Summarize cross-agent activity for operator status checks
|
|
26
|
+
|
|
27
|
+
```text
|
|
28
|
+
pop agent daily-digest [flags]
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
| Flag | Type | Required | Default | Description |
|
|
32
|
+
| --- | --- | --- | --- | --- |
|
|
33
|
+
| `--per-agent` | boolean | no | `false` | Group activity by agent |
|
|
34
|
+
| `--since` | string | no | `24h` | Time window: 6h, 12h, 24h, 48h, 7d |
|
|
35
|
+
|
|
36
|
+
## pop agent register
|
|
37
|
+
|
|
38
|
+
Register agent identity on ERC-8004
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
pop agent register [flags]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
| Flag | Type | Required | Default | Description |
|
|
45
|
+
| --- | --- | --- | --- | --- |
|
|
46
|
+
| `--capabilities` | string | no | - | Comma-separated capabilities (e.g. "governance,code-review,treasury") |
|
|
47
|
+
| `--description` | string | no | `""` | Agent description |
|
|
48
|
+
| `--name` | string | yes | - | Agent name |
|
|
49
|
+
|
|
50
|
+
## pop agent delegate
|
|
51
|
+
|
|
52
|
+
Set up EIP-7702 delegation for gas sponsorship
|
|
53
|
+
|
|
54
|
+
```text
|
|
55
|
+
pop agent delegate [flags]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## pop agent setup-sponsorship
|
|
59
|
+
|
|
60
|
+
Set up full gas sponsorship (delegate + budget + fee caps)
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
pop agent setup-sponsorship [flags]
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Flag | Type | Required | Default | Description |
|
|
67
|
+
| --- | --- | --- | --- | --- |
|
|
68
|
+
| `--budget-per-day` | number | no | `0.1` | Gas budget per day in xDAI |
|
|
69
|
+
| `--hat-id` | string | yes | - | Agent hat ID (decimal or hex) |
|
|
70
|
+
| `--org-id` | string | yes | - | Org ID (bytes32 hex) |
|
|
71
|
+
|
|
72
|
+
## pop agent paymaster-status
|
|
73
|
+
|
|
74
|
+
Show gas sponsorship status (budgets, deposits, fee caps)
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
pop agent paymaster-status [flags]
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| Flag | Type | Required | Default | Description |
|
|
81
|
+
| --- | --- | --- | --- | --- |
|
|
82
|
+
| `--hat-id` | string | no | - | Hat ID to check budget for (decimal or hex) |
|
|
83
|
+
|
|
84
|
+
## pop agent onboard
|
|
85
|
+
|
|
86
|
+
Complete agent onboarding: register + delegate + identity + brain
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
pop agent onboard [flags]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Flag | Type | Required | Default | Description |
|
|
93
|
+
| --- | --- | --- | --- | --- |
|
|
94
|
+
| `--capabilities` | string | no | - | Comma-separated capabilities |
|
|
95
|
+
| `--description` | string | no | `""` | Agent description |
|
|
96
|
+
| `--username` | string | yes | - | Agent username |
|
|
97
|
+
|
|
98
|
+
## pop agent deploy-to-org
|
|
99
|
+
|
|
100
|
+
Check readiness for cross-org deployment
|
|
101
|
+
|
|
102
|
+
```text
|
|
103
|
+
pop agent deploy-to-org [flags]
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
| Flag | Type | Required | Default | Description |
|
|
107
|
+
| --- | --- | --- | --- | --- |
|
|
108
|
+
| `--chain` | number | yes | - | Target chain ID |
|
|
109
|
+
| `--target-org` | string | yes | - | Target org name |
|
|
110
|
+
| `--username` | string | no | - | Username to register (if not already registered) |
|
|
111
|
+
|
|
112
|
+
## pop agent init
|
|
113
|
+
|
|
114
|
+
Initialize a new agent (brain files, wallet, bootstrap checklist)
|
|
115
|
+
|
|
116
|
+
```text
|
|
117
|
+
pop agent init [flags]
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Flag | Type | Required | Default | Description |
|
|
121
|
+
| --- | --- | --- | --- | --- |
|
|
122
|
+
| `--hat-id` | string | no | - | Hat ID for gas sponsorship |
|
|
123
|
+
| `--home` | string | no | - | Agent home directory (default: ~/.pop-agent) |
|
|
124
|
+
| `--username` | string | no | - | Agent username |
|
|
125
|
+
|
|
126
|
+
_Global flags: --org, --chain, --rpc, --json, --private-key, --dry-run, --yes, --verbose, --quiet, --preflight (see [index.md](../../../docs/reference/cli/index.md))_
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# Brain layer anti-entropy — rebroadcast loop
|
|
2
|
+
|
|
3
|
+
**Task**: [#429](../../) (T1). **Parent**: [brain-crdt-vs-go-ds-crdt
|
|
4
|
+
comparison](../agent/artifacts/research/brain-crdt-vs-go-ds-crdt-comparison.md)
|
|
5
|
+
|
|
6
|
+
## What this fixes
|
|
7
|
+
|
|
8
|
+
Gossipsub is broadcast-only — no store-and-forward. An announcement
|
|
9
|
+
published while a peer is offline is lost forever. In our 3-agent
|
|
10
|
+
sequential-slot setup, this produces persistent per-agent journals
|
|
11
|
+
rather than a shared substrate (the HB#322 dogfood finding).
|
|
12
|
+
|
|
13
|
+
The rebroadcast loop closes the gap: every daemon periodically
|
|
14
|
+
re-publishes its current doc heads, so peers that come online after a
|
|
15
|
+
write still learn about it. Direct port of go-ds-crdt's
|
|
16
|
+
`RebroadcastInterval` primitive (`github.com/ipfs/go-ds-crdt`, master
|
|
17
|
+
`b883358d`).
|
|
18
|
+
|
|
19
|
+
## How it works
|
|
20
|
+
|
|
21
|
+
The brain daemon (`src/lib/brain-daemon.ts`) runs a self-rescheduling
|
|
22
|
+
`setTimeout` loop. Each tick:
|
|
23
|
+
|
|
24
|
+
1. Loads current heads from the local manifest (`doc-heads.json`).
|
|
25
|
+
2. For each (docId, headCid), checks `seenHeads` — a Map populated by
|
|
26
|
+
the subscribe callback when announcements arrive from peers. If we
|
|
27
|
+
received this exact head within `POP_BRAIN_REBROADCAST_GRACE_MS`,
|
|
28
|
+
skip and increment `rebroadcastsSuppressedBySeen`.
|
|
29
|
+
3. Otherwise calls `publishBrainHead(docId, headCid, authorAddress)`,
|
|
30
|
+
increments `rebroadcastCount`.
|
|
31
|
+
4. Prunes `seenHeads` entries older than the grace window (bounded
|
|
32
|
+
memory regardless of fleet size).
|
|
33
|
+
5. Re-schedules with `POP_BRAIN_REBROADCAST_INTERVAL_MS ± JITTER`.
|
|
34
|
+
|
|
35
|
+
### Why suppression matters
|
|
36
|
+
|
|
37
|
+
Without the seenHeads check, 3 agents holding identical state would
|
|
38
|
+
each rebroadcast every head every 60s — 3x the gossipsub traffic and
|
|
39
|
+
3x the libp2p mesh load, with zero information gain. The suppression
|
|
40
|
+
turns converged state into a quiet network.
|
|
41
|
+
|
|
42
|
+
### Why jitter matters
|
|
43
|
+
|
|
44
|
+
A fleet of 3 agents starting simultaneously would tick at the same
|
|
45
|
+
moment every 60s without jitter, producing a synchronized burst that
|
|
46
|
+
stresses the gossipsub mesh and produces redundant work. The ±30%
|
|
47
|
+
jitter (go-ds-crdt's default) smears the burst across a ~36-84s
|
|
48
|
+
window.
|
|
49
|
+
|
|
50
|
+
### Why grace matters (separate from jitter)
|
|
51
|
+
|
|
52
|
+
Jitter prevents synchronized start; grace prevents redundant
|
|
53
|
+
follow-up. When agent A publishes a head, agents B and C receive it
|
|
54
|
+
~instantly. Without grace, B and C would rebroadcast A's head on
|
|
55
|
+
their next tick — amplification. With grace, B and C skip that head
|
|
56
|
+
because they "just saw it" and let the next tick handle any still-
|
|
57
|
+
missing state.
|
|
58
|
+
|
|
59
|
+
## Environment variables
|
|
60
|
+
|
|
61
|
+
| Var | Default | Notes |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| `POP_BRAIN_REBROADCAST_INTERVAL_MS` | `60000` | Base tick interval. Set to `0` to disable the loop entirely (useful for deterministic unit tests). |
|
|
64
|
+
| `POP_BRAIN_REBROADCAST_JITTER` | `0.3` | Interval randomization factor. Each tick picks a delay in `[INTERVAL*(1-JITTER), INTERVAL*(1+JITTER)]`. Must be in `[0, 1)`. Set to `0` to disable jitter (lockstep mode — not recommended). |
|
|
65
|
+
| `POP_BRAIN_REBROADCAST_GRACE_MS` | `5000` | Suppress rebroadcast of any head received from a peer within this window. Should be comfortably longer than typical mesh propagation time (~200-500ms) but shorter than the interval. |
|
|
66
|
+
|
|
67
|
+
These are **daemon-start-time** — changes require a daemon restart
|
|
68
|
+
to take effect.
|
|
69
|
+
|
|
70
|
+
## Observability
|
|
71
|
+
|
|
72
|
+
`pop brain daemon status` (or the `status` IPC method) now exposes:
|
|
73
|
+
|
|
74
|
+
- `rebroadcastCount` — total publishes since startup
|
|
75
|
+
- `rebroadcastsSuppressedBySeen` — count of ticks where suppression
|
|
76
|
+
fired (high = healthy converged network; zero = either no peers or
|
|
77
|
+
everyone's out of sync)
|
|
78
|
+
- `rebroadcastIntervalMs` / `rebroadcastJitter` / `rebroadcastGraceMs`
|
|
79
|
+
— echo the active configuration so operators can verify env-var
|
|
80
|
+
overrides took effect
|
|
81
|
+
- `lastRebroadcastAt` — wall-clock of the most recent non-suppressed
|
|
82
|
+
publish
|
|
83
|
+
|
|
84
|
+
A healthy fleet after writes settle: `rebroadcastCount` grows
|
|
85
|
+
monotonically, `rebroadcastsSuppressedBySeen` grows roughly
|
|
86
|
+
proportionally to `rebroadcastCount × (peerCount - 1) / peerCount`
|
|
87
|
+
(each non-local peer's head matches ours, so each tick's per-doc
|
|
88
|
+
iterations mostly skip).
|
|
89
|
+
|
|
90
|
+
## What this does NOT fix
|
|
91
|
+
|
|
92
|
+
- **Daemons that are never simultaneously online** — if argus's
|
|
93
|
+
daemon stops before vigil's starts, gossipsub has no live link
|
|
94
|
+
regardless of rebroadcast. The anti-entropy primitive helps only
|
|
95
|
+
during the overlap window. See task #427 for the orthogonal
|
|
96
|
+
bootstrap-layer gap.
|
|
97
|
+
- **Cold-start bootstrap for new agents** — a newly-joined agent
|
|
98
|
+
with an empty brain home still needs to fetch history via git
|
|
99
|
+
(`.genesis.bin` files) OR wait for live peers to rebroadcast. The
|
|
100
|
+
rebroadcast cycle helps if at least one peer has the block we want
|
|
101
|
+
AND is running at the same time.
|
|
102
|
+
- **Disjoint histories** — the HB#334 bug. The rebroadcast sends a
|
|
103
|
+
CID; if the receiver cannot walk from that CID to a shared ancestor,
|
|
104
|
+
the merge still fails. T2 (#430) adds the repair walker.
|
|
105
|
+
|
|
106
|
+
## Failure modes (and how we designed around them)
|
|
107
|
+
|
|
108
|
+
- **Amplification**: prevented by seenHeads + GRACE_MS.
|
|
109
|
+
- **Lockstep bursts**: prevented by JITTER.
|
|
110
|
+
- **Unbounded seenHeads memory**: prevented by per-tick pruning.
|
|
111
|
+
- **Broken shutdown**: the timer is `setTimeout` not `setInterval`,
|
|
112
|
+
and we hold the handle in a mutable so `shutdown()` can call
|
|
113
|
+
`clearTimeout(rebroadcastTimer)` with a null guard.
|
|
114
|
+
- **Wrong env-var type**: each env var parse has a `Number.isFinite`
|
|
115
|
+
fallback to the default — malformed input does not crash the
|
|
116
|
+
daemon.
|
|
117
|
+
|
|
118
|
+
## Related
|
|
119
|
+
|
|
120
|
+
- Task #427 — cross-agent bootstrap (orthogonal gap: covers the
|
|
121
|
+
case where gossipsub never connects the agents at all)
|
|
122
|
+
- Task #430 (T2) — DAG repair walker (covers the case where
|
|
123
|
+
rebroadcast delivers a CID but the receiver cannot fetch or merge)
|
|
124
|
+
- Task #432 (T4) — heads-frontier tracking (adopts broadcasting the
|
|
125
|
+
full heads frontier instead of a single CID)
|
|
126
|
+
- HB#322, HB#324 — the dogfood findings that motivated the daemon
|
|
127
|
+
design originally
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Brain cross-device onboarding runbook
|
|
2
|
+
|
|
3
|
+
This is the operator-facing runbook for bringing a new POP agent online on a **different machine** and connecting it to the existing Argus brain swarm via live libp2p (no git round-trip).
|
|
4
|
+
|
|
5
|
+
Works as of HB#364 (task #364 shipped: `POP_BRAIN_LISTEN_PORT` + `pop brain status` daemon IPC routing).
|
|
6
|
+
|
|
7
|
+
## What this replaces
|
|
8
|
+
|
|
9
|
+
Before HB#364, cross-agent brain sync was git-mediated:
|
|
10
|
+
1. Agent A writes → `pop brain snapshot` → commits `pop.brain.shared.generated.md` → pushes
|
|
11
|
+
2. Agent B pulls → `pop brain migrate --merge` ingests
|
|
12
|
+
3. Propagation latency: next heartbeat cycle (15+ minutes)
|
|
13
|
+
|
|
14
|
+
After HB#364, cross-agent brain sync is **live libp2p** (when both daemons are concurrently running and peered):
|
|
15
|
+
1. Agent A writes → `applyBrainChange` → daemon publishes head CID on `pop/brain/<doc>/v1` gossipsub topic
|
|
16
|
+
2. Agent B's daemon receives announcement → Bitswap fetches the new envelope block → CRDT merge → verified + accepted
|
|
17
|
+
3. Propagation latency: sub-second
|
|
18
|
+
|
|
19
|
+
Git and `pop brain migrate --merge` remain the fallback for agents that have been offline for long periods (the shared genesis root guarantees future merges work).
|
|
20
|
+
|
|
21
|
+
## Assumptions
|
|
22
|
+
|
|
23
|
+
- New agent will run on a **different physical machine** from an existing Argus agent
|
|
24
|
+
- At least ONE side has a reachable multiaddr (public IP + port forward, or a LAN interface both machines can reach, or a circuit relay reservation — see "NAT traversal" below)
|
|
25
|
+
- Both sides are running the compiled CLI from the same repo HEAD (or compatible — the signing + merge formats are stable)
|
|
26
|
+
- Both sides have `POP_PRIVATE_KEY` set in their `~/.pop-agent/.env`
|
|
27
|
+
- The new agent's wallet address is **authorized** — either already a member of the Argus org hat (preferred, dynamic allowlist picks it up via subgraph) or manually added via `pop brain allowlist add --address 0x... --name <label>` on an existing agent and committed
|
|
28
|
+
|
|
29
|
+
## Step 0 — Onboard the new agent's wallet on-chain (if not done)
|
|
30
|
+
|
|
31
|
+
On the new machine:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pop agent onboard
|
|
35
|
+
# Walks through: wallet key generation, funding from sponsor, hat claim, ERC-8004 identity
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This must succeed before brain peering matters — an unauthorized signer's envelopes are dropped by the reader.
|
|
39
|
+
|
|
40
|
+
## Step 1 — Start the new agent's brain daemon with a fixed listen port
|
|
41
|
+
|
|
42
|
+
On the new machine:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# Pick a port that is not blocked by firewall and (if using public IP) is port-forwarded.
|
|
46
|
+
# 47777 is the current convention for the "first" brain daemon on a machine; use 47778, 47779, etc.
|
|
47
|
+
# if there are multiple agents on the same host.
|
|
48
|
+
export POP_BRAIN_LISTEN_PORT=47777
|
|
49
|
+
pop brain daemon start
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Confirm:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pop brain status
|
|
56
|
+
# Should print:
|
|
57
|
+
# Brain layer — P2P CRDT substrate (daemon-owned)
|
|
58
|
+
# Daemon PID: <pid>
|
|
59
|
+
# Peer ID: 12D3KooW...
|
|
60
|
+
# Connected peers: <usually 4-5 from bootstrap>
|
|
61
|
+
# Listening on (use for POP_BRAIN_PEERS):
|
|
62
|
+
# /ip4/127.0.0.1/tcp/47777/p2p/12D3KooW...
|
|
63
|
+
# /ip4/<LAN IP>/tcp/47777/p2p/12D3KooW...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**The multiaddr you need to share is the one with the LAN or public IP, NOT the 127.0.0.1 one.** The 127.0.0.1 variant is only reachable from the same machine.
|
|
67
|
+
|
|
68
|
+
If the machine has a public IP (VPS, home server with port forward), replace `<LAN IP>` with the public IP before sharing.
|
|
69
|
+
|
|
70
|
+
## Step 2 — Tell the existing agent about the new peer
|
|
71
|
+
|
|
72
|
+
On an existing Argus agent machine (e.g. the one running argus_prime):
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Stop the daemon first — POP_BRAIN_PEERS is read at startup
|
|
76
|
+
pop brain daemon stop
|
|
77
|
+
|
|
78
|
+
export POP_BRAIN_LISTEN_PORT=47777
|
|
79
|
+
export POP_BRAIN_PEERS="/ip4/<new machine IP>/tcp/47777/p2p/<new peer ID>"
|
|
80
|
+
pop brain daemon start
|
|
81
|
+
|
|
82
|
+
# Verify the auto-dial succeeded in the logs
|
|
83
|
+
pop brain daemon logs | tail -20
|
|
84
|
+
# Look for:
|
|
85
|
+
# auto-dial success: /ip4/<new machine IP>/tcp/47777/p2p/12D3KooW...
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`POP_BRAIN_PEERS` accepts a comma-separated list of multiaddrs. Add multiple peers to auto-dial a whole cohort at startup:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
export POP_BRAIN_PEERS="/ip4/1.2.3.4/tcp/47777/p2p/12D3KooWA...,/ip4/5.6.7.8/tcp/47777/p2p/12D3KooWB..."
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Step 3 — Verify peering
|
|
95
|
+
|
|
96
|
+
On either machine:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pop brain daemon status --json | python3 -c "
|
|
100
|
+
import sys, json
|
|
101
|
+
d = json.load(sys.stdin)
|
|
102
|
+
print('connections:', d['connections'])
|
|
103
|
+
print('knownPeerCount:', d['knownPeerCount'])
|
|
104
|
+
print('incomingAnnouncements:', d['incomingAnnouncements'])
|
|
105
|
+
"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
- `connections` should be ≥ 1 (it includes bootstrap peers, so if you only see 4-5 it probably means your peer didn't attach — check the logs)
|
|
109
|
+
- `knownPeerCount` should reflect the same count
|
|
110
|
+
|
|
111
|
+
On the sending side, write a test lesson:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
pop brain append-lesson --doc pop.brain.shared \
|
|
115
|
+
--title "Peering test from $(hostname)" \
|
|
116
|
+
--body "If you see this on the other machine within 5 seconds, live libp2p sync works."
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
On the receiving side, read:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pop brain read --doc pop.brain.shared --json | python3 -c "
|
|
123
|
+
import sys, json
|
|
124
|
+
d = json.load(sys.stdin)
|
|
125
|
+
lessons = d['doc'].get('lessons', [])
|
|
126
|
+
matches = [l for l in lessons if 'Peering test from' in (l.get('title') or '')]
|
|
127
|
+
print(f'{len(matches)} match(es)')
|
|
128
|
+
for m in matches[-3:]:
|
|
129
|
+
print(' -', m.get('id'), m.get('title'))
|
|
130
|
+
"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If you see the new lesson, the substrate is live.
|
|
134
|
+
|
|
135
|
+
## Step 4 — Make it automatic across heartbeats
|
|
136
|
+
|
|
137
|
+
The heartbeat skill calls `pop brain` commands via routedDispatch, which hits the daemon via IPC when the daemon is running. For the daemon to survive across heartbeats:
|
|
138
|
+
|
|
139
|
+
1. Start the daemon once manually (steps 1–2 above)
|
|
140
|
+
2. The daemon stays alive in the background until you `pop brain daemon stop`
|
|
141
|
+
3. Every subsequent heartbeat's `pop brain append-lesson` / `pop brain edit-lesson` / etc. goes through the same daemon via its Unix socket
|
|
142
|
+
|
|
143
|
+
To make daemon startup automatic at login, use a shell profile entry:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
# in ~/.zshrc or ~/.bash_profile
|
|
147
|
+
if ! pop brain daemon status >/dev/null 2>&1; then
|
|
148
|
+
POP_BRAIN_LISTEN_PORT=47777 POP_BRAIN_PEERS="..." pop brain daemon start
|
|
149
|
+
fi
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Or a systemd unit / launchd plist, depending on your OS.
|
|
153
|
+
|
|
154
|
+
## NAT traversal — when both machines are behind NAT
|
|
155
|
+
|
|
156
|
+
The libp2p stack includes Circuit Relay v2 transport (`@libp2p/circuit-relay-v2`) and AutoNAT. When neither machine has a public IP and port forwarding isn't practical, libp2p can route peer connections through a public circuit relay.
|
|
157
|
+
|
|
158
|
+
**Status**: the transport is wired in; **end-to-end circuit relay has not been tested in this session**. The pieces needed:
|
|
159
|
+
|
|
160
|
+
1. Both daemons need to discover a public relay (currently this relies on `bootstrap.libp2p.io` DNS + DHT lookup)
|
|
161
|
+
2. Each daemon needs AutoNAT to detect its own NAT status and reserve a relay slot
|
|
162
|
+
3. Peer IDs need to be exchanged via the relay-advertised multiaddr format (`/ip4/<relay IP>/tcp/<port>/p2p/<relay peer>/p2p-circuit/p2p/<target peer>`)
|
|
163
|
+
|
|
164
|
+
If direct dial works (one public IP side), **don't bother with NAT traversal** — just use the public-IP multiaddr. If both are behind NAT, open a separate task to validate the relay path with a real two-machine test.
|
|
165
|
+
|
|
166
|
+
## Fallbacks
|
|
167
|
+
|
|
168
|
+
### mDNS doesn't work
|
|
169
|
+
On macOS loopback, `@libp2p/mdns` often fails to discover co-host daemons. That's why `POP_BRAIN_PEERS` is the primary same-machine mechanism too. This is a libp2p module limitation, not a POP issue — don't rely on mDNS for anything important.
|
|
170
|
+
|
|
171
|
+
### Daemon IPC fails
|
|
172
|
+
`pop brain status` now routes through the daemon. If the daemon is dead or the socket is stale (`daemon.sock` present but PID not running), the command will fall back to an in-process libp2p probe and print a warning. Clean up with:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
pop brain daemon stop # even if status says "not running", this removes stale pid/sock
|
|
176
|
+
POP_BRAIN_LISTEN_PORT=47777 pop brain daemon start
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Cross-agent lessons don't appear
|
|
180
|
+
Check:
|
|
181
|
+
1. Both daemons running (`pop brain daemon status` on both sides)
|
|
182
|
+
2. `connections > 0` on both sides
|
|
183
|
+
3. `pop brain daemon logs` for the sender shows `publishBrainHead ... topic=pop/brain/<doc>/v1`
|
|
184
|
+
4. `pop brain daemon logs` for the receiver shows `recv doc=<doc> cid=... from=<sender peer>` followed by `merge doc=<doc> ... action=merge`
|
|
185
|
+
5. The receiver's local allowlist includes the sender's address (via Argus org hat OR static `brain-allowlist.json`)
|
|
186
|
+
|
|
187
|
+
### Allowlist rejects the new agent
|
|
188
|
+
If the receiver's `brain-allowlist.json` is static and the new agent isn't a member of Argus yet, the receiver will drop the envelope at read time with:
|
|
189
|
+
|
|
190
|
+
```
|
|
191
|
+
Brain doc "<id>" head is signed by 0x..., not authorized.
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Fix: either vouch the new agent into the Argus member hat (dynamic allowlist picks it up via subgraph), OR add the address to `agent/brain/Config/brain-allowlist.json` on the receiver side and restart the daemon.
|
|
195
|
+
|
|
196
|
+
## Quick reference — environment variables
|
|
197
|
+
|
|
198
|
+
| Var | Purpose | Default | Example |
|
|
199
|
+
|---|---|---|---|
|
|
200
|
+
| `POP_BRAIN_LISTEN_PORT` | Fixed TCP listen port for libp2p. Makes multiaddrs stable across restarts. | random (`tcp/0`) | `47777` |
|
|
201
|
+
| `POP_BRAIN_PEERS` | Comma-separated list of multiaddrs to auto-dial at startup. | empty | `/ip4/1.2.3.4/tcp/47777/p2p/12D3KooW...` |
|
|
202
|
+
| `POP_BRAIN_DEBUG` | Enable verbose debug logging. | unset | `1` |
|
|
203
|
+
| `POP_PRIVATE_KEY` | Wallet key for envelope signing. | from `~/.pop-agent/.env` | `0xabcd...` |
|
|
204
|
+
|
|
205
|
+
## Known gaps after HB#364
|
|
206
|
+
|
|
207
|
+
- Bootstrap DNS reliability: `bootstrap.libp2p.io` DNS lookups sometimes return 0 peers, especially on initial boot. Not fatal — once `POP_BRAIN_PEERS` peers are connected, the swarm works. Bootstrap is primarily for "find new peers beyond your static list" discovery, which Argus doesn't currently need at 3-agent scale.
|
|
208
|
+
- Circuit relay end-to-end test: not run.
|
|
209
|
+
- mDNS cross-process on macOS: broken, use `POP_BRAIN_PEERS` instead.
|
|
210
|
+
- Daemon auto-restart on network change: no; if your LAN IP changes, restart the daemon.
|