Skip to content

Commit 6a606d7

Browse files
committed
Enterprise Phase 2: audit trail, webhook reliability, invite consent, fleet enrollment, network CLI
- Webhook reliability: monotonic event IDs, 3-retry exponential backoff, dropped counter, drain timeout - Registry audit events: structured audit() calls in 18 mutation handlers (slog JSON, filterable via jq) - Invite-requires-acceptance: pending invite inbox, poll/respond flow, persistence in snapshots - Daemon auto-join: config-driven fleet enrollment via --admin-token and --networks flags - pilotctl network commands: list, join, leave, members, invite, accept, reject via IPC - Dashboard: remove network names from Polo API and HTML - README: fix misleading claims (zero dependencies, no servers), clarify rendezvous architecture - Website: correct marketing copy across homepage, docs, and 10+ blog posts - Console/admin binaries added to gitignore - 845 tests passing (13 new test files, 44+ new tests)
1 parent 06592f6 commit 6a606d7

74 files changed

Lines changed: 15081 additions & 230 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,11 @@ web/research/
111111

112112
# Newsletter worker
113113
newsletter/
114+
115+
# Console & admin (private — not public)
116+
console
117+
console-ui/
118+
cmd/console/
119+
cmd/pilot-admin/
120+
pkg/console/
121+
*.db

Makefile

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: all build test clean vet ci release coverage coverage-html
1+
.PHONY: all build test clean vet ci release coverage coverage-html console-ui build-console
22

33
BINDIR := bin
44
COVERDIR := coverage
@@ -105,3 +105,17 @@ release:
105105
rm -rf $(BINDIR)/release/$$os-$$arch; \
106106
done
107107
@echo "Release archives in $(BINDIR)/release/"
108+
109+
# Console (web management UI) — requires CGo for SQLite
110+
console-ui:
111+
cd console-ui && npm ci && npm run build
112+
rm -rf pkg/console/ui
113+
cp -r console-ui/build pkg/console/ui
114+
115+
build-console: console-ui
116+
@mkdir -p $(BINDIR)
117+
CGO_ENABLED=1 go build -ldflags "$(LDFLAGS)" -o $(BINDIR)/console ./cmd/console
118+
119+
build-console-linux: console-ui
120+
@mkdir -p $(BINDIR)
121+
CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o $(BINDIR)/console-linux ./cmd/console

README.md

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<p align="center">
88
<strong>The network stack for AI agents.</strong><br>
9-
Addresses. Ports. Tunnels. Encryption. Trust. Zero dependencies.
9+
Addresses. Ports. Tunnels. Encryption. Trust.
1010
</p>
1111

1212
<p align="center">
@@ -27,9 +27,9 @@
2727

2828
<p align="center">
2929
<img src="https://img.shields.io/badge/lang-Go-00ADD8?logo=go&logoColor=white" alt="Go">
30-
<img src="https://img.shields.io/badge/deps-zero-brightgreen" alt="Zero Dependencies">
30+
<img src="https://img.shields.io/badge/deps-stdlib_only-brightgreen" alt="Standard Library Only">
3131
<img src="https://img.shields.io/badge/encryption-AES--256--GCM-blueviolet" alt="Encryption">
32-
<img src="https://img.shields.io/badge/tests-683%20pass-success" alt="Tests">
32+
<img src="https://img.shields.io/badge/tests-845%20pass-success" alt="Tests">
3333
<a href="https://www.ietf.org/archive/id/draft-teodor-pilot-protocol-00.html"><img src="https://img.shields.io/badge/IETF-Internet--Draft-blue" alt="IETF Internet-Draft"></a>
3434
<img src="https://img.shields.io/badge/license-AGPL--3.0-blue" alt="License">
3535
<img src="https://polo.pilotprotocol.network/api/badge/nodes" alt="Online Nodes">
@@ -44,15 +44,15 @@
4444
<img src="docs/media/pilot-demo.gif" alt="Pilot Protocol Demo — two agents: install, trust, data exchange" width="960">
4545
</p>
4646

47-
The internet was built for humans. AI agents have no address, no identity, no way to be reached. Pilot Protocol is an overlay network that gives agents what the internet gave devices: **a permanent address, encrypted peer-to-peer channels, and a trust model** -- all layered on top of standard UDP.
47+
The internet was built for humans. AI agents have no address, no identity, no way to be reached. Pilot Protocol is an overlay network that gives agents what the internet gave devices: **a permanent address, authenticated encrypted channels, and a trust model** -- all layered on top of standard UDP.
4848

49-
It is not an API. It is not a framework. It is infrastructure.
49+
Agents register with a rendezvous service for discovery and NAT traversal. Application data flows directly between peers -- never through a central server. It is not an API. It is not a framework. It is infrastructure.
5050

5151
---
5252

5353
## The problem
5454

55-
Today, agents talk through centralized APIs. Every connection requires a platform in the middle. There is no way for two agents to find each other, establish trust, or communicate directly.
55+
Today, agents talk through centralized APIs. Every message passes through a platform -- the platform sees all traffic, controls access, and becomes a single point of failure.
5656

5757
```mermaid
5858
graph LR
@@ -65,16 +65,20 @@ graph LR
6565
style A3 fill:#4a9,stroke:#333,color:#fff
6666
```
6767

68-
Pilot Protocol removes the middleman. Each agent gets a permanent address and talks directly to peers over encrypted tunnels:
68+
Pilot Protocol takes the platform out of the data path. A lightweight **rendezvous** service handles discovery and NAT traversal, but once agents find each other, they talk directly over authenticated, encrypted tunnels:
6969

7070
```mermaid
7171
graph LR
7272
A1[Agent A<br/><small>0:0000.0000.0001</small>] <-->|Encrypted UDP Tunnel| A2[Agent B<br/><small>0:0000.0000.0002</small>]
7373
A1 <-->|Encrypted UDP Tunnel| A3[Agent C<br/><small>0:0000.0000.0003</small>]
7474
A2 <-->|Encrypted UDP Tunnel| A3
75+
A1 -.->|discovery| RV[Rendezvous]
76+
A2 -.->|discovery| RV
77+
A3 -.->|discovery| RV
7578
style A1 fill:#4a9,stroke:#333,color:#fff
7679
style A2 fill:#4a9,stroke:#333,color:#fff
7780
style A3 fill:#4a9,stroke:#333,color:#fff
81+
style RV fill:#888,stroke:#333,color:#fff
7882
```
7983

8084
---
@@ -161,8 +165,8 @@ $ pilotctl --json find nonexistent
161165
<td width="50%" valign="top">
162166

163167
**Security**
164-
- Encrypt-by-default (X25519 + AES-256-GCM)
165-
- Ed25519 identities with persistence
168+
- Authenticated key exchange (Ed25519-signed X25519 + AES-256-GCM)
169+
- Ed25519 identity keys bound to tunnel sessions
166170
- Nodes are private by default
167171
- Mutual trust handshake protocol (signed, relay via registry)
168172

@@ -210,7 +214,9 @@ graph LR
210214
end
211215
```
212216

213-
Your agent talks to a local **daemon** over a Unix socket. The daemon handles tunnel encryption, NAT traversal, packet routing, congestion control, and built-in services. The daemon connects to a **rendezvous** server (registry + beacon) for node discovery and NAT hole-punching.
217+
Your agent talks to a local **daemon** over a Unix socket. The daemon handles tunnel encryption, NAT traversal, packet routing, congestion control, and built-in services. The daemon maintains a connection to a **rendezvous** server (registry + beacon) for node registration, peer discovery, and NAT hole-punching. Once a tunnel is established, data flows directly between daemons -- the rendezvous is not in the data path.
218+
219+
A public rendezvous is provided at `34.71.57.205:9000`, or you can run your own with `rendezvous -registry-addr :9000 -beacon-addr :9001`.
214220

215221
For connection lifecycle details, gateway bridging, and NAT traversal strategy, see the [full documentation](https://pilotprotocol.network/docs/).
216222

@@ -293,7 +299,7 @@ See the [Python SDK documentation](https://pilotprotocol.network/docs/python-sdk
293299
go test -parallel 4 -count=1 ./tests/
294300
```
295301

296-
683 tests pass, 26 skipped (IPv6, platform-specific). The `-parallel 4` flag is required -- unlimited parallelism exhausts ports and causes dial timeouts.
302+
845 tests pass. The `-parallel 4` flag is required -- unlimited parallelism exhausts ports and causes dial timeouts.
297303

298304
---
299305

cmd/daemon/main.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import (
66
"log/slog"
77
"os"
88
"os/signal"
9+
"strconv"
10+
"strings"
911
"syscall"
1012

1113
"github.com/TeoSlayer/pilotprotocol/pkg/config"
@@ -39,6 +41,8 @@ func main() {
3941
noEventStream := flag.Bool("no-eventstream", false, "disable built-in event stream service (port 1002)")
4042
noTaskSubmit := flag.Bool("no-tasksubmit", false, "disable built-in task submit service (port 1003)")
4143
webhookURL := flag.String("webhook", "", "HTTP(S) endpoint for event notifications (empty = disabled)")
44+
adminToken := flag.String("admin-token", "", "admin token for network operations")
45+
networks := flag.String("networks", "", "comma-separated network IDs to auto-join at startup")
4246
logLevel := flag.String("log-level", "info", "log level (debug, info, warn, error)")
4347
logFormat := flag.String("log-format", "text", "log format (text, json)")
4448
flag.Parse()
@@ -78,6 +82,8 @@ func main() {
7882
DisableEventStream: *noEventStream,
7983
DisableTaskSubmit: *noTaskSubmit,
8084
WebhookURL: *webhookURL,
85+
AdminToken: *adminToken,
86+
Networks: parseNetworkIDs(*networks),
8187
})
8288

8389
if err := d.Start(); err != nil {
@@ -92,3 +98,25 @@ func main() {
9298
slog.Info("shutting down")
9399
d.Stop()
94100
}
101+
102+
// parseNetworkIDs parses a comma-separated string of network IDs into a uint16 slice.
103+
func parseNetworkIDs(s string) []uint16 {
104+
if s == "" {
105+
return nil
106+
}
107+
parts := strings.Split(s, ",")
108+
var ids []uint16
109+
for _, p := range parts {
110+
p = strings.TrimSpace(p)
111+
if p == "" {
112+
continue
113+
}
114+
n, err := strconv.ParseUint(p, 10, 16)
115+
if err != nil {
116+
log.Printf("warning: invalid network ID %q: %v", p, err)
117+
continue
118+
}
119+
ids = append(ids, uint16(n))
120+
}
121+
return ids
122+
}

0 commit comments

Comments
 (0)