@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.
Files changed (139) hide show
  1. package/.env.agent.template +20 -0
  2. package/README.md +46 -0
  3. package/brain/Config/agent-config.json +14 -0
  4. package/brain/Config/brain-allowlist.json +20 -0
  5. package/brain/Identity/goals.template.md +23 -0
  6. package/brain/Identity/how-i-think.md +406 -0
  7. package/brain/Identity/who-i-am.template.md +34 -0
  8. package/brain/Knowledge/BOOTSTRAP.md +66 -0
  9. package/brain/Knowledge/audit-corpus-index.json +406 -0
  10. package/brain/Knowledge/discussions.json +245 -0
  11. package/brain/Knowledge/pop.brain.brainstorms.generated.md +48 -0
  12. package/brain/Knowledge/pop.brain.brainstorms.genesis.bin +0 -0
  13. package/brain/Knowledge/pop.brain.heuristics.snapshot.bin +0 -0
  14. package/brain/Knowledge/pop.brain.projects.generated.md +16 -0
  15. package/brain/Knowledge/pop.brain.projects.genesis.bin +0 -0
  16. package/brain/Knowledge/pop.brain.retros.generated.md +91 -0
  17. package/brain/Knowledge/pop.brain.retros.genesis.bin +0 -0
  18. package/brain/Knowledge/pop.brain.shared.generated.md +3811 -0
  19. package/brain/Knowledge/pop.brain.shared.genesis.bin +0 -0
  20. package/brain/Knowledge/projects.md +181 -0
  21. package/brain/Knowledge/risk-framework.md +90 -0
  22. package/brain/Knowledge/shared.md +416 -0
  23. package/brain/Knowledge/sprint-priorities.md +439 -0
  24. package/brain/Memory/.gitkeep +0 -0
  25. package/dist/commands/agent/daily-digest.d.ts +24 -0
  26. package/dist/commands/agent/daily-digest.js +336 -0
  27. package/dist/commands/agent/delegate.d.ts +12 -0
  28. package/dist/commands/agent/delegate.js +91 -0
  29. package/dist/commands/agent/deploy-to-org.d.ts +20 -0
  30. package/dist/commands/agent/deploy-to-org.js +154 -0
  31. package/dist/commands/agent/index.d.ts +2 -0
  32. package/dist/commands/agent/index.js +27 -0
  33. package/dist/commands/agent/init.d.ts +19 -0
  34. package/dist/commands/agent/init.js +303 -0
  35. package/dist/commands/agent/onboard.d.ts +22 -0
  36. package/dist/commands/agent/onboard.js +192 -0
  37. package/dist/commands/agent/paymaster-status.d.ts +14 -0
  38. package/dist/commands/agent/paymaster-status.js +130 -0
  39. package/dist/commands/agent/register.d.ts +21 -0
  40. package/dist/commands/agent/register.js +116 -0
  41. package/dist/commands/agent/setup-sponsorship.d.ts +22 -0
  42. package/dist/commands/agent/setup-sponsorship.js +154 -0
  43. package/dist/commands/agent/status.d.ts +12 -0
  44. package/dist/commands/agent/status.js +171 -0
  45. package/dist/commands/agent/triage.d.ts +12 -0
  46. package/dist/commands/agent/triage.js +503 -0
  47. package/dist/commands/brain/advance-stage.d.ts +42 -0
  48. package/dist/commands/brain/advance-stage.js +206 -0
  49. package/dist/commands/brain/allowlist.d.ts +30 -0
  50. package/dist/commands/brain/allowlist.js +274 -0
  51. package/dist/commands/brain/append-lesson.d.ts +55 -0
  52. package/dist/commands/brain/append-lesson.js +245 -0
  53. package/dist/commands/brain/brainstorm.d.ts +154 -0
  54. package/dist/commands/brain/brainstorm.js +573 -0
  55. package/dist/commands/brain/daemon.d.ts +31 -0
  56. package/dist/commands/brain/daemon.js +348 -0
  57. package/dist/commands/brain/doctor.d.ts +27 -0
  58. package/dist/commands/brain/doctor.js +497 -0
  59. package/dist/commands/brain/edit-lesson.d.ts +51 -0
  60. package/dist/commands/brain/edit-lesson.js +248 -0
  61. package/dist/commands/brain/import-snapshot.d.ts +68 -0
  62. package/dist/commands/brain/import-snapshot.js +177 -0
  63. package/dist/commands/brain/index.d.ts +2 -0
  64. package/dist/commands/brain/index.js +67 -0
  65. package/dist/commands/brain/list.d.ts +21 -0
  66. package/dist/commands/brain/list.js +83 -0
  67. package/dist/commands/brain/migrate-projects.d.ts +44 -0
  68. package/dist/commands/brain/migrate-projects.js +209 -0
  69. package/dist/commands/brain/migrate.d.ts +74 -0
  70. package/dist/commands/brain/migrate.js +306 -0
  71. package/dist/commands/brain/new-project.d.ts +53 -0
  72. package/dist/commands/brain/new-project.js +226 -0
  73. package/dist/commands/brain/read.d.ts +24 -0
  74. package/dist/commands/brain/read.js +81 -0
  75. package/dist/commands/brain/remove-lesson.d.ts +47 -0
  76. package/dist/commands/brain/remove-lesson.js +206 -0
  77. package/dist/commands/brain/remove-project.d.ts +36 -0
  78. package/dist/commands/brain/remove-project.js +177 -0
  79. package/dist/commands/brain/retro-file-tasks.d.ts +84 -0
  80. package/dist/commands/brain/retro-file-tasks.js +372 -0
  81. package/dist/commands/brain/retro-list.d.ts +28 -0
  82. package/dist/commands/brain/retro-list.js +125 -0
  83. package/dist/commands/brain/retro-mark-change.d.ts +58 -0
  84. package/dist/commands/brain/retro-mark-change.js +176 -0
  85. package/dist/commands/brain/retro-remove.d.ts +36 -0
  86. package/dist/commands/brain/retro-remove.js +142 -0
  87. package/dist/commands/brain/retro-respond.d.ts +56 -0
  88. package/dist/commands/brain/retro-respond.js +250 -0
  89. package/dist/commands/brain/retro-show.d.ts +23 -0
  90. package/dist/commands/brain/retro-show.js +100 -0
  91. package/dist/commands/brain/retro-start.d.ts +55 -0
  92. package/dist/commands/brain/retro-start.js +311 -0
  93. package/dist/commands/brain/search.d.ts +48 -0
  94. package/dist/commands/brain/search.js +190 -0
  95. package/dist/commands/brain/snapshot.d.ts +32 -0
  96. package/dist/commands/brain/snapshot.js +243 -0
  97. package/dist/commands/brain/status.d.ts +15 -0
  98. package/dist/commands/brain/status.js +166 -0
  99. package/dist/commands/brain/subscribe.d.ts +28 -0
  100. package/dist/commands/brain/subscribe.js +90 -0
  101. package/dist/commands/brain/tag.d.ts +46 -0
  102. package/dist/commands/brain/tag.js +192 -0
  103. package/dist/index.d.ts +17 -0
  104. package/dist/index.js +22 -0
  105. package/dist/lib/brain-daemon.d.ts +126 -0
  106. package/dist/lib/brain-daemon.js +811 -0
  107. package/dist/lib/brain-membership.d.ts +58 -0
  108. package/dist/lib/brain-membership.js +115 -0
  109. package/dist/lib/brain-migrate-projects.d.ts +43 -0
  110. package/dist/lib/brain-migrate-projects.js +247 -0
  111. package/dist/lib/brain-migrate.d.ts +77 -0
  112. package/dist/lib/brain-migrate.js +328 -0
  113. package/dist/lib/brain-ops.d.ts +271 -0
  114. package/dist/lib/brain-ops.js +571 -0
  115. package/dist/lib/brain-paths.d.ts +15 -0
  116. package/dist/lib/brain-paths.js +33 -0
  117. package/dist/lib/brain-projections.d.ts +216 -0
  118. package/dist/lib/brain-projections.js +829 -0
  119. package/dist/lib/brain-schemas.d.ts +36 -0
  120. package/dist/lib/brain-schemas.js +316 -0
  121. package/dist/lib/brain-signing.d.ts +103 -0
  122. package/dist/lib/brain-signing.js +256 -0
  123. package/dist/lib/brain.d.ts +198 -0
  124. package/dist/lib/brain.js +1057 -0
  125. package/dist/pop-agent.d.ts +1 -0
  126. package/dist/pop-agent.js +18 -0
  127. package/docs/agent.md +126 -0
  128. package/docs/agents/brain-anti-entropy.md +127 -0
  129. package/docs/agents/brain-cross-device-onboarding.md +210 -0
  130. package/docs/agents/brain-cross-machine-smoke.md +241 -0
  131. package/docs/agents/brain-layer-setup.md +725 -0
  132. package/docs/agents/offboarding-protocol.md +188 -0
  133. package/docs/agents/onboarding-protocol.md +243 -0
  134. package/docs/agents/running-an-agent.md +200 -0
  135. package/docs/brain.md +560 -0
  136. package/package.json +61 -0
  137. package/scripts/apply.sh +140 -0
  138. package/scripts/onboard.sh +205 -0
  139. 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.