@dp-pcs/ogp 0.8.3 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +14 -0
  2. package/dist/cli/config.d.ts +1 -1
  3. package/dist/cli/config.d.ts.map +1 -1
  4. package/dist/cli/config.js +17 -1
  5. package/dist/cli/config.js.map +1 -1
  6. package/dist/cli/federation.d.ts +20 -3
  7. package/dist/cli/federation.d.ts.map +1 -1
  8. package/dist/cli/federation.js +131 -20
  9. package/dist/cli/federation.js.map +1 -1
  10. package/dist/cli/project.d.ts +12 -0
  11. package/dist/cli/project.d.ts.map +1 -1
  12. package/dist/cli/project.js +110 -2
  13. package/dist/cli/project.js.map +1 -1
  14. package/dist/cli/tunnel.d.ts +7 -1
  15. package/dist/cli/tunnel.d.ts.map +1 -1
  16. package/dist/cli/tunnel.js +12 -3
  17. package/dist/cli/tunnel.js.map +1 -1
  18. package/dist/cli.js +107 -21
  19. package/dist/cli.js.map +1 -1
  20. package/dist/daemon/message-handler.d.ts.map +1 -1
  21. package/dist/daemon/message-handler.js +73 -1
  22. package/dist/daemon/message-handler.js.map +1 -1
  23. package/dist/daemon/project-ownership.d.ts +47 -0
  24. package/dist/daemon/project-ownership.d.ts.map +1 -0
  25. package/dist/daemon/project-ownership.js +109 -0
  26. package/dist/daemon/project-ownership.js.map +1 -0
  27. package/dist/daemon/projects.d.ts +8 -0
  28. package/dist/daemon/projects.d.ts.map +1 -1
  29. package/dist/daemon/projects.js +89 -0
  30. package/dist/daemon/projects.js.map +1 -1
  31. package/dist/shared/meta-config.d.ts.map +1 -1
  32. package/dist/shared/meta-config.js +23 -9
  33. package/dist/shared/meta-config.js.map +1 -1
  34. package/docs/CLI-REFERENCE.md +38 -0
  35. package/docs/TRANSPORT-MODES-DESIGN.md +164 -0
  36. package/package.json +1 -1
  37. package/scripts/completion.bash +2 -2
  38. package/scripts/completion.zsh +4 -0
@@ -5,6 +5,7 @@ Complete command-line reference for OGP (Open Gateway Protocol).
5
5
  ## Table of Contents
6
6
 
7
7
  - [Global Options](#global-options)
8
+ - [Environment Variables](#environment-variables)
8
9
  - [Setup and Configuration](#setup-and-configuration)
9
10
  - [Daemon Management](#daemon-management)
10
11
  - [Federation Commands](#federation-commands)
@@ -49,6 +50,10 @@ ogp --for all status
49
50
  - If multiple frameworks are configured and no default is set, prompts interactively
50
51
  - If a default framework is set, uses default (can override with `--for`)
51
52
  - `--for all` runs command on all enabled frameworks and aggregates output
53
+ - If the meta-registry is empty (no `ogp setup` was run) but `OGP_HOME` is set,
54
+ `--for <framework>` honors `OGP_HOME` and prints a warning instead of failing.
55
+ This is the common case in server/container deployments where the daemon is
56
+ launched by setting `OGP_HOME` directly. See [Environment Variables](#environment-variables).
52
57
 
53
58
  ### --help, -h
54
59
 
@@ -70,6 +75,39 @@ Shows OGP version.
70
75
  ogp --version
71
76
  ```
72
77
 
78
+ ## Environment Variables
79
+
80
+ OGP does **not** hardcode its storage location. The default is `~/.ogp`, but
81
+ every path is resolved at runtime and can be overridden via environment
82
+ variables. This is the supported way to run OGP in containers, on servers
83
+ (ECS/nginx), or anywhere `$HOME` is not where you want config to live — no code
84
+ changes or recompilation required.
85
+
86
+ | Variable | Overrides | Default |
87
+ | --- | --- | --- |
88
+ | `OGP_HOME` | Per-framework data dir: `config.json`, `peers.json`, identity, keychain. **The daemon only needs this.** | `~/.ogp` |
89
+ | `OGP_META_HOME` | The multi-framework meta-registry (`config.json` listing frameworks for `--for`). | `~/.ogp-meta` |
90
+
91
+ **Server / container deployment (recommended):**
92
+ ```bash
93
+ # Point both the daemon and the CLI at the same writable location.
94
+ export OGP_HOME=/data/ogp # or e.g. /home/node/.openclaw/.ogp
95
+ ogp start --background
96
+ ogp peers list # CLI inherits OGP_HOME, talks to the daemon
97
+ ```
98
+
99
+ **Common pitfall:** If the daemon is launched with `OGP_HOME` set but you then
100
+ run `ogp --for openclaw ...`, the CLI looks up `openclaw` in the meta-registry
101
+ (`OGP_META_HOME`), which was never populated by an interactive `ogp setup`.
102
+ As of this version, `--for` falls back to `OGP_HOME` with a warning instead of
103
+ erroring. The clean fix is to **not pass `--for` on a single-home server** — just
104
+ export `OGP_HOME` and let every `ogp` invocation inherit it.
105
+
106
+ **$HOME mismatch:** If the daemon and CLI run under different users/`$HOME`
107
+ values (common in containers), the meta-registry can silently "not exist" for
108
+ one of them. Set `OGP_META_HOME` (and `OGP_HOME`) explicitly so both processes
109
+ resolve the same paths.
110
+
73
111
  ## Setup and Configuration
74
112
 
75
113
  ### ogp setup
@@ -0,0 +1,164 @@
1
+ # Transport Modes Design
2
+
3
+ **Status:** 🚧 Proposed — under review. The rendezvous registration schema change is adjacent to the Ed25519 trust model and should be reviewed carefully before merge.
4
+
5
+ **Date:** 2026-06-08
6
+
7
+ ## Problem
8
+
9
+ OGP's biggest adoption deterrent is the tunnel requirement. Today peers deliver
10
+ Ed25519-signed envelopes by **direct HTTP POST to each other's public
11
+ `gatewayUrl`**, and the rendezvous server is a pubkey→address directory that
12
+ hands back a raw `ip:port`. Even *with* rendezvous, a peer must be publicly
13
+ reachable — which is exactly the job the cloudflared/ngrok tunnel does.
14
+
15
+ The tunnel is **not a transport requirement — it's a reachability hack.** We want
16
+ to make reachability a *user choice*, so people who don't want to run a tunnel can
17
+ opt into a relay instead, without forcing anyone already on tunnels to change.
18
+
19
+ ## Key insight that makes this safe
20
+
21
+ **OGP messages are already Ed25519-signed end-to-end.** Any relay/transport in the
22
+ middle is *untrusted by design* — it can see envelope metadata but cannot forge,
23
+ alter, or impersonate. This radically lowers the security bar for the transport
24
+ layer and makes "route through a relay" perfectly acceptable. This single fact is
25
+ what makes a pluggable transport possible without weakening the trust model.
26
+
27
+ ## Goal
28
+
29
+ A per-user, per-daemon `transport` setting that lets each operator choose how their
30
+ daemon is reached, defaulting to **today's behavior** so no existing setup breaks:
31
+
32
+ - **`direct`** (default) — public `gatewayUrl` via tunnel / public IP / port-forward. Exactly what works today. Zero change for current users.
33
+ - **`relay`** — daemon holds a persistent outbound WebSocket to a relay; messages route peer→relay→peer. No inbound port, no tunnel. User picks *which* relay (ours by default, or a self-hosted one).
34
+ - **`iroh`** — daemon uses Iroh (QUIC). ~90% of pairs get a direct P2P connection; the rest fall back through a relay. E2E-encrypted regardless. Self-hostable relay. The deliberate pilot.
35
+
36
+ ## User experience
37
+
38
+ ```bash
39
+ # Default — nothing changes for existing users
40
+ ogp config get transport.mode # => direct
41
+
42
+ # Opt into relay (no tunnel needed)
43
+ ogp config set transport.mode relay
44
+ ogp config set transport.relay.url wss://relay.example.com/relay # default if omitted
45
+
46
+ # Power user / privacy: self-hosted relay
47
+ ogp config set transport.relay.url wss://relay.mycorp.internal
48
+
49
+ # Pilot iroh
50
+ ogp config set transport.mode iroh
51
+ ogp config set transport.iroh.relayUrl https://my-dedicated-relay # omit = public dev relays
52
+ ```
53
+
54
+ A mixed fleet works: each peer advertises *how* to reach it, and senders branch on
55
+ the **receiver's** advertised transport (see schema change below).
56
+
57
+ ## Config shape
58
+
59
+ Add a `transport` block to `OGPConfig` (`src/shared/config.ts`). Absent ⇒ `direct`.
60
+
61
+ ```ts
62
+ export type TransportMode = 'direct' | 'relay' | 'iroh';
63
+
64
+ export interface TransportConfig {
65
+ mode: TransportMode; // default 'direct'
66
+ relay?: {
67
+ url: string; // websocket relay endpoint; default wss://<rendezvous>/relay
68
+ };
69
+ iroh?: {
70
+ relayUrl?: string; // dedicated/self-hosted iroh relay; omit = public dev relays
71
+ };
72
+ }
73
+
74
+ // OGPConfig:
75
+ // transport?: TransportConfig;
76
+ ```
77
+
78
+ ## The keystone change — rendezvous advertises *how*, not just *where*
79
+
80
+ This is the one structural change everything else depends on, and the part that
81
+ **must be reviewed carefully before merge** (it sits next to the signed
82
+ registration / trust model).
83
+
84
+ Today the rendezvous `/register` stores `{ pubkey, ip, port, lastSeen }` and
85
+ `/peer/:pubkey` returns `{ pubkey, ip, port, lastSeen }`. We extend the registered
86
+ record with a **transport descriptor** so a sender knows which path to use:
87
+
88
+ ```jsonc
89
+ // direct (default / backward compatible — missing descriptor ⇒ direct)
90
+ { "transport": "direct", "gatewayUrl": "https://peer.example.com" }
91
+
92
+ // relay
93
+ { "transport": "relay", "relayUrl": "wss://relay.example.com/relay" }
94
+
95
+ // iroh
96
+ { "transport": "iroh", "nodeId": "<iroh-node-id>", "relayUrl": "https://..." }
97
+ ```
98
+
99
+ Rules:
100
+ - **Additive & backward compatible.** A registration with no descriptor is treated
101
+ as `direct` with the existing `ip:port` / `gatewayUrl`. Old daemons keep working.
102
+ - The descriptor rides **inside the existing signed registration envelope**
103
+ (`signCanonical` over the inner payload) so the rendezvous can't be tricked into
104
+ advertising a transport the keyholder didn't choose. **This is the trust-model
105
+ touchpoint — review carefully before merge.**
106
+ - Senders branch delivery on the peer's advertised `transport`, not their own.
107
+
108
+ ## Relay server (mode = `relay`)
109
+
110
+ Extends the existing rendezvous service (Node/Express on ECS Fargate). No new box.
111
+
112
+ - New `wss://<rendezvous>/relay` endpoint. Each daemon opens a **persistent
113
+ outbound** WebSocket and authenticates by signing a challenge with its Ed25519
114
+ key (proving pubkey ownership, same property as `/register`).
115
+ - Routing table: `pubkey → live socket`. To deliver, sender pushes
116
+ `{ to: pubkeyB, envelope }`; server forwards down B's socket. Optional
117
+ store-and-forward queue for offline peers, flushed on reconnect.
118
+ - **ECS/ALB gotcha (confirmed):** ALB idle timeout defaults to 60s, max 4000s. We
119
+ **must** send app-level WS ping/heartbeat (~30–50s) or idle sockets get dropped.
120
+ - **Horizontal scale:** one Node process handles tens of thousands of idle
121
+ sockets; past one Fargate task we need a shared routing table (Redis pub/sub) so
122
+ a message arriving on task-1 reaches a socket on task-2.
123
+ - Server stays **untrusted** — it sees envelopes but can't forge them (E2E Ed25519).
124
+
125
+ ## Iroh (mode = `iroh`) — pilot notes
126
+
127
+ - QUIC, node-ID addressing. Relays do (1) NAT-traversal coordination and (2)
128
+ encrypted fallback; **relays cannot read traffic** (E2E). ~9/10 pairs get a
129
+ direct connection; traversal is deterministic once it works for a pair.
130
+ - Relays are **stateless/disposable** (no DB, no state migration, reconnect to any).
131
+ - **Cost/effort caveats:** Iroh is Rust; OGP is Node. Official **NAPI Node bindings**
132
+ exist (`iroh` npm), so it's a native-addon dependency with per-platform prebuilds,
133
+ not a full rewrite — but it *is* a new native dep for a daemon that ships daily.
134
+ Public relays are **dev/test only** (rate-limited, no SLA); production = dedicated
135
+ relays (self-hosted open-source binary from n0-computer, or n0's paid Iroh
136
+ Services). Addressing changes from URL → node ID, so the delivery path is rewired.
137
+ - Treat as **opt-in pilot**, not default. Once the transport descriptor exists,
138
+ iroh is "just another transport value," not a rewrite-or-nothing bet.
139
+
140
+ Sources: https://docs.iroh.computer/concepts/relays ,
141
+ https://docs.aws.amazon.com/elasticloadbalancing/latest/application/edit-load-balancer-attributes.html
142
+
143
+ ## Phased build plan
144
+
145
+ 1. **Transport descriptor in register/lookup** (additive, backward compatible;
146
+ missing ⇒ `direct`). Unblocks everything, low risk. **Schema/trust touchpoint —
147
+ review carefully before merge.**
148
+ 2. **`relay` mode** — WebSocket through rendezvous + signed-challenge auth +
149
+ ALB heartbeat. Lowest code; kills the tunnel requirement; opt-in dogfooding.
150
+ 3. **`iroh` mode pilot** — once relay is proven, add iroh as another descriptor
151
+ value behind a dedicated/self-hosted relay.
152
+
153
+ ## Hard rules / guardrails
154
+
155
+ - **Default stays `direct`** — never silently change how existing users' traffic moves.
156
+ - **No auto-deploy** of the rendezvous/relay server — changes are proposed and reviewed, not shipped automatically.
157
+ - **Registration-schema + relay-auth code requires careful review before merge** — it's adjacent to the Ed25519 trust model, which is the product.
158
+ - E2E Ed25519 signing is preserved in **every** mode; the relay is always untrusted.
159
+
160
+ ## Non-goals (for now)
161
+
162
+ - Replacing direct mode. It stays as the privacy/no-third-party path.
163
+ - Picking iroh vs websocket-relay as the *single* answer. The point is choice +
164
+ real-world dogfooding before committing a default beyond `direct`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dp-pcs/ogp",
3
- "version": "0.8.3",
3
+ "version": "0.9.1",
4
4
  "description": "Open Gateway Protocol (OGP) - Peer-to-peer federation daemon for OpenClaw AI gateways with cryptographic signatures",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -61,7 +61,7 @@ _ogp_completion() {
61
61
  # agent-comms subcommands
62
62
  if [ "$cmd" = "agent-comms" ]; then
63
63
  if [ $COMP_CWORD -eq 2 ]; then
64
- opts="policies configure add-topic set-topic set-default remove-topic reset activity default logging"
64
+ opts="send policies configure add-topic set-topic set-default remove-topic reset activity default logging"
65
65
  COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
66
66
  return 0
67
67
  fi
@@ -116,7 +116,7 @@ _ogp_completion() {
116
116
  # project subcommands
117
117
  if [ "$cmd" = "project" ]; then
118
118
  if [ $COMP_CWORD -eq 2 ]; then
119
- opts="create join list remove contribute query status request-join send-contribution query-peer status-peer delete"
119
+ opts="create join list remove contribute query status request-join send-contribution query-peer status-peer delete add-owner claim-ownership owners"
120
120
  COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
121
121
  return 0
122
122
  fi
@@ -238,6 +238,7 @@ _ogp_agent_comms() {
238
238
 
239
239
  _arguments \
240
240
  '1:subcommand:((
241
+ send\:"Send an agent-comms message to a peer (alias of federation agent)"
241
242
  policies\:"Show response policies"
242
243
  configure\:"Configure response policies"
243
244
  add-topic\:"Add topic to peer response policy"
@@ -432,6 +433,9 @@ _ogp_project() {
432
433
  query-peer\:"Query peer project contributions"
433
434
  status-peer\:"Request project status from peer"
434
435
  delete\:"Delete local project and all contributions"
436
+ add-owner\:"Grant ownership to a peer key (owners only)"
437
+ claim-ownership\:"Claim ownership of a pre-existing project (members only)"
438
+ owners\:"List the owners of a project"
435
439
  ))' \
436
440
  '*::arg:->args'
437
441