Skip to content

Commit 7c5f790

Browse files
committed
fix(daemon): satisfy daemonapi.Daemon contract after common@v0.4.3 bump
The latest common (v0.4.3) added Sign to daemonapi.Daemon, Info to daemonapi.Connection, and tightened PortAllocator.Bind to return daemonapi.Listener instead of *Listener. PR #155's adapter (written against earlier daemonapi) no longer satisfied the interface, breaking `go build ./...` for cmd/daemon and tests/testenv. Changes: - Add *Daemon.Sign(msg) []byte — forwards to identity.Sign with nil guard for pre-bootstrap and in-memory test cases. - Add *Connection.Info() daemonapi.ConnectionInfo — endpoint snapshot for plugin consumption (struct copy, holdable across goroutines). - Add portAllocatorAdapter + listenerAdapter wrappers so Bind returns the daemonapi.Listener interface and Accept/Port/Close go through channel/field-shaped engine methods. - Wire d.DaemonAPI() once in cmd/daemon/main.go and tests/testenv.go, thread the shared value into runtime.New / NewPolicyRuntime / NewHandshakeRuntime instead of passing engine-typed *Daemon. - Bump go.mod to latest sibling pseudo-versions / betas. Verified: go build ./... + go vet ./... clean; pilot-daemon binary runs and prints --help.
1 parent f352561 commit 7c5f790

5 files changed

Lines changed: 115 additions & 8 deletions

File tree

cmd/daemon/main.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,15 @@ func main() {
189189
// L11 plugin lifecycle (T7.1): composition root owns the
190190
// ServiceRegistry via plugins/runtime. Daemon never imports
191191
// pkg/coreapi.
192-
rt := runtime.New(d)
192+
//
193+
// runtime + the per-plugin Runtime constructors take a
194+
// daemonapi.Daemon. *Daemon doesn't satisfy that interface
195+
// directly (engine-typed Connection/PortAllocator/etc.); the
196+
// adapter at pkg/daemon/zz_daemonapi_conformance.go does. We
197+
// resolve it once via d.DaemonAPI() and thread the shared value
198+
// everywhere — keeps the type assertion in one place.
199+
dapi := d.DaemonAPI()
200+
rt := runtime.New(dapi)
193201

194202
ta := trustedagents.NewService()
195203
if err := rt.Register(ta); err != nil {
@@ -215,14 +223,14 @@ func main() {
215223
}
216224
}
217225

218-
policySvc := policy.NewService(runtime.NewPolicyRuntime(d))
226+
policySvc := policy.NewService(runtime.NewPolicyRuntime(dapi))
219227
if err := rt.Register(policySvc); err != nil {
220228
log.Fatalf("register policy: %v", err)
221229
}
222230
d.RegisterPolicyManager(runtime.AsDaemonPolicyManager(policySvc.Manager()))
223231

224232
// Manual trust-handshake (port 444) — extracted from pkg/daemon in T3.3.
225-
hsSvc := handshake.NewService(runtime.NewHandshakeRuntime(d))
233+
hsSvc := handshake.NewService(runtime.NewHandshakeRuntime(dapi))
226234
if err := rt.Register(hsSvc); err != nil {
227235
log.Fatalf("register handshake: %v", err)
228236
}

go.mod

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.25.10
44

55
require (
66
github.com/coder/websocket v1.8.14
7+
<<<<<<< HEAD
78
github.com/pilot-protocol/app-store v0.1.0
89
github.com/pilot-protocol/beacon v0.1.0
910
github.com/pilot-protocol/common v0.4.4
@@ -17,6 +18,21 @@ require (
1718
github.com/pilot-protocol/skillinject v0.1.0
1819
github.com/pilot-protocol/trustedagents v0.1.0
1920
github.com/pilot-protocol/webhook v0.1.0
21+
=======
22+
github.com/pilot-protocol/app-store v1.0.1-beta.1
23+
github.com/pilot-protocol/beacon v0.2.3-0.20260529143248-4f632d9c0953
24+
github.com/pilot-protocol/common v0.4.3
25+
github.com/pilot-protocol/dataexchange v0.2.1-beta.1
26+
github.com/pilot-protocol/eventstream v0.2.2
27+
github.com/pilot-protocol/handshake v0.2.1-0.20260529034908-6f286b1fa5c8
28+
github.com/pilot-protocol/nameserver v0.2.1
29+
github.com/pilot-protocol/policy v0.2.1
30+
github.com/pilot-protocol/rendezvous v0.2.2-0.20260529153356-45c9d2195c20
31+
github.com/pilot-protocol/runtime v0.3.1-0.20260529034924-948bcb891b24
32+
github.com/pilot-protocol/skillinject v0.2.2-0.20260529041510-e36bc173e7e7
33+
github.com/pilot-protocol/trustedagents v0.2.3-beta.3
34+
github.com/pilot-protocol/webhook v0.2.1-0.20260529034934-0d9094bbdede
35+
>>>>>>> 20c941f (fix(daemon): satisfy daemonapi.Daemon contract after common@v0.4.3 bump)
2036
)
2137

2238
require (
@@ -49,7 +65,6 @@ replace github.com/pilot-protocol/app-store => ../app-store
4965

5066
replace github.com/pilot-protocol/updater => ../updater
5167

52-
replace github.com/pilot-protocol/common => ../common
5368

5469
replace github.com/pilot-protocol/handshake => ../handshake
5570

pkg/daemon/daemon.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,6 +2002,21 @@ func (d *Daemon) Identity() *crypto.Identity {
20022002
return d.identity
20032003
}
20042004

2005+
// Sign signs msg with the daemon's Ed25519 private key. Returns nil
2006+
// when no identity is loaded (in-memory tests, pre-bootstrap).
2007+
//
2008+
// Required by daemonapi.Daemon (common@v0.4.3). The adapter at
2009+
// zz_daemonapi_conformance.go forwards to this directly.
2010+
func (d *Daemon) Sign(msg []byte) []byte {
2011+
d.identityMu.RLock()
2012+
id := d.identity
2013+
d.identityMu.RUnlock()
2014+
if id == nil {
2015+
return nil
2016+
}
2017+
return id.Sign(msg)
2018+
}
2019+
20052020
// RotateKey generates a new Ed25519 keypair, proves ownership of the current
20062021
// key to the registry via a `rotate:<node_id>` signature, atomically swaps the
20072022
// in-memory identity on success, and persists it to disk. Returns the

pkg/daemon/zz_daemonapi_conformance.go

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,67 @@ import (
1212
registry "github.com/pilot-protocol/common/registry/client"
1313
)
1414

15+
// --- Connection.Info ------------------------------------------------
16+
//
17+
// daemonapi.Connection requires Info() ConnectionInfo. Bolted onto
18+
// *Connection here (rather than ports.go) so the I/O-heavy types in
19+
// ports.go stay free of plugin-API concerns and the interface
20+
// fulfilment lives next to its adapter siblings.
21+
22+
// Info returns an endpoint snapshot for plugin consumption. Required
23+
// by daemonapi.Connection (common@v0.4.3).
24+
func (c *Connection) Info() daemonapi.ConnectionInfo {
25+
return daemonapi.ConnectionInfo{
26+
LocalAddr: c.LocalAddr,
27+
LocalPort: c.LocalPort,
28+
RemoteAddr: c.RemoteAddr,
29+
RemotePort: c.RemotePort,
30+
}
31+
}
32+
33+
// --- PortAllocator + Listener adapters ------------------------------
34+
//
35+
// daemonapi.PortAllocator wants Bind to return daemonapi.Listener.
36+
// daemonapi.Listener wants Accept/Port/Close methods. The concrete
37+
// *PortManager and *Listener types use channel-shaped APIs (TrySend,
38+
// AcceptCh) that pre-date daemonapi, so we wrap them here without
39+
// changing the engine's call sites.
40+
41+
// portAllocatorAdapter wraps *PortManager so its Bind returns the
42+
// daemonapi.Listener interface (via a listenerAdapter) instead of
43+
// the daemon-engine-typed *Listener.
44+
type portAllocatorAdapter struct{ pm *PortManager }
45+
46+
func (a portAllocatorAdapter) Bind(port uint16) (daemonapi.Listener, error) {
47+
ln, err := a.pm.Bind(port)
48+
if err != nil {
49+
return nil, err
50+
}
51+
return listenerAdapter{ln: ln}, nil
52+
}
53+
54+
func (a portAllocatorAdapter) Unbind(port uint16) { a.pm.Unbind(port) }
55+
56+
// listenerAdapter wraps *Listener so it satisfies daemonapi.Listener:
57+
// channel-shaped AcceptCh becomes an Accept() method, the Port field
58+
// becomes a Port() method, and close() becomes Close().
59+
type listenerAdapter struct{ ln *Listener }
60+
61+
func (a listenerAdapter) Accept() (daemonapi.Connection, bool) {
62+
conn, ok := <-a.ln.AcceptCh
63+
if !ok {
64+
return nil, false
65+
}
66+
return conn, true
67+
}
68+
69+
func (a listenerAdapter) Port() uint16 { return a.ln.Port }
70+
71+
func (a listenerAdapter) Close() error {
72+
a.ln.close()
73+
return nil
74+
}
75+
1576
// daemonAPIAdapter wraps a *Daemon to satisfy daemonapi.Daemon.
1677
//
1778
// Why an adapter instead of changing *Daemon's method signatures:
@@ -49,6 +110,7 @@ func (a daemonAPIAdapter) Stop() error { return a.d.Stop() }
49110
func (a daemonAPIAdapter) NodeID() uint32 { return a.d.NodeID() }
50111
func (a daemonAPIAdapter) Identity() *crypto.Identity { return a.d.Identity() }
51112
func (a daemonAPIAdapter) IdentityPath() string { return a.d.IdentityPath() }
113+
func (a daemonAPIAdapter) Sign(msg []byte) []byte { return a.d.Sign(msg) }
52114

53115
// --- Configuration ---------------------------------------------------
54116

@@ -95,7 +157,9 @@ func (a daemonAPIAdapter) NewConnReadWriter(c daemonapi.Connection) daemonapi.Co
95157
return a.d.NewConnReadWriter(conn)
96158
}
97159

98-
func (a daemonAPIAdapter) Ports() daemonapi.PortAllocator { return a.d.Ports() }
160+
func (a daemonAPIAdapter) Ports() daemonapi.PortAllocator {
161+
return portAllocatorAdapter{pm: a.d.Ports()}
162+
}
99163
func (a daemonAPIAdapter) Tunnels() daemonapi.TunnelRegistry { return a.d.Tunnels() }
100164

101165
// --- Registry --------------------------------------------------------

tests/testenv.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,12 @@ func (env *TestEnv) AddDaemonOnly(opts ...func(*daemon.Config)) (*daemon.Daemon,
218218
// corresponding plugin is skipped. Returns the runtime so callers
219219
// can call StartPlugins/StopPlugins around d.Start/d.Stop.
220220
func registerStandardPlugins(t testingT, d *daemon.Daemon, cfg *daemon.Config) *pluginsruntime.Runtime {
221-
rt := pluginsruntime.New(d)
221+
// runtime + per-plugin Runtime constructors expect daemonapi.Daemon.
222+
// *Daemon satisfies that interface via the adapter at
223+
// pkg/daemon/zz_daemonapi_conformance.go; we resolve it once and
224+
// thread the shared value everywhere — same shape as cmd/daemon.
225+
dapi := d.DaemonAPI()
226+
rt := pluginsruntime.New(dapi)
222227
if !cfg.DisableDataExchange {
223228
if err := rt.Register(dataexchange.NewService(dataexchange.ServiceConfig{})); err != nil {
224229
t.Fatalf("register dataexchange: %v", err)
@@ -230,7 +235,7 @@ func registerStandardPlugins(t testingT, d *daemon.Daemon, cfg *daemon.Config) *
230235
}
231236
}
232237
if !cfg.DisablePolicyRunner {
233-
policySvc := policy.NewService(pluginsruntime.NewPolicyRuntime(d))
238+
policySvc := policy.NewService(pluginsruntime.NewPolicyRuntime(dapi))
234239
if err := rt.Register(policySvc); err != nil {
235240
t.Fatalf("register policy: %v", err)
236241
}
@@ -239,7 +244,7 @@ func registerStandardPlugins(t testingT, d *daemon.Daemon, cfg *daemon.Config) *
239244
// Handshake plugin (T3.3) — registered by default so production
240245
// behavior matches: tests that don't want it can flip a Disable*
241246
// flag on Config (none today, since smoke + e2e all need handshake).
242-
hsSvc := handshake.NewService(pluginsruntime.NewHandshakeRuntime(d))
247+
hsSvc := handshake.NewService(pluginsruntime.NewHandshakeRuntime(dapi))
243248
if err := rt.Register(hsSvc); err != nil {
244249
t.Fatalf("register handshake: %v", err)
245250
}

0 commit comments

Comments
 (0)