Documentation
¶
Overview ¶
Package node is the Agent Wire Protocol engine: it owns the identity key, the durable store, the listeners and every connection, and implements the handshake, resume, acks, blobs, grants and reconnection of SPEC.md.
Index ¶
- Constants
- func NewThreadID() string
- type Config
- type Conn
- type ConnInfo
- type ExecRequest
- type FSReadRequest
- type FSWriteRequest
- type Node
- func (n *Node) Activity() (last time.Time, waiting bool)
- func (n *Node) Addresses() []string
- func (n *Node) Bye(peer, reason string) error
- func (n *Node) Caps(peer string) []string
- func (n *Node) Changed() <-chan struct{}
- func (n *Node) Close() error
- func (n *Node) ConnInfo(key string) *ConnInfo
- func (n *Node) Connect(ctx context.Context, addr string) (string, error)
- func (n *Node) Connected(key string) bool
- func (n *Node) Dialing(key string) (bool, string)
- func (n *Node) Grant(sub string, caps []string, ttl time.Duration) (*store.GrantRow, error)
- func (n *Node) Harness() string
- func (n *Node) Home() string
- func (n *Node) Introduce(to, intro string, caps []string, ttl time.Duration, th string) (*SendResult, error)
- func (n *Node) Key() string
- func (n *Node) LocalPresence() (*wire.Presence, error)
- func (n *Node) MakePrivate(peer, th string) error
- func (n *Node) Model() string
- func (n *Node) Name() string
- func (n *Node) ResolvePeer(s string) (string, error)
- func (n *Node) ResolveThread(th string) (string, error)
- func (n *Node) Revoke(hash string) (int64, error)
- func (n *Node) Send(req SendRequest) (*SendResult, error)
- func (n *Node) SetModel(m string) (bool, error)
- func (n *Node) SetShareWith(keys []string) error
- func (n *Node) SetState(peer, th, state, note string) (*SendResult, error)
- func (n *Node) ShareWith() []string
- func (n *Node) SharesPresence() bool
- func (n *Node) Start() error
- func (n *Node) Store() *store.Store
- func (n *Node) TailcatAddress() (addr, errMsg string)
- func (n *Node) Touch()
- func (n *Node) Waiting() (done func())
- type Policy
- type Request
- type SendRequest
- type SendResult
Constants ¶
const ( // AcceptAny admits any key with the default capabilities. It is what // the spec recommends for a coding agent on a sandbox. AcceptAny = "any" // AcceptAllowlist admits only allow-listed or trusted keys, keys we // have dialed ourselves, and keys that present an honored grant. AcceptAllowlist = "allowlist" )
Admission policies (section 7.3).
const ( CapExec = "exec" CapFSRead = "fs:read" CapFSWrite = "fs:write" CapIntroduce = "introduce" CapAdmin = "admin" )
Capabilities with a meaning in this implementation (section 10.1).
const ( MimeExecResult = "application/vnd.awp.exec-result+json" MimeFSReadResult = "application/vnd.awp.fs-read-result+json" MimeFSWriteResult = "application/vnd.awp.fs-write-result+json" )
Result mime types for served requests (PROFILE.md).
const (
PresenceStaleAfter = 150 * time.Second // no heartbeat for this long: stale
)
Presence gossip: an opt-in, signed summary of what each agent is doing, forwarded across the network so that a watcher on any connected host can see all of it (wire/presence.go, NOTES.md "Presence").
Variables ¶
This section is empty.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Home is the state directory (~/.awp by default in the CLI).
Home string
// Name and About are sent in hello.
Name string
About string
// Model is the model the agent runs on (claude-opus-5-5, gpt-5.5, ...),
// shared in presence. The agent or its harness hooks report it, and it
// can change while the node runs (SetModel). Remembered like the name.
Model string
// to (wire/mirror.go). Nil keeps what was set before; empty stops.
ShareWith []string
// Harness is the agent harness this node runs in (internal/harness),
// shared in presence. Remembered like the name.
Harness string
// DetectedHarness is the fallback when no harness was ever set: the one
// the environment of whoever started the node points to. It is not
// remembered, since the next start may come from another harness.
DetectedHarness string
// Listen lists addresses to accept connections on: "tailcat",
// "tcp:host:port" or "unix:/path".
Listen []string
// Advertise is the address sent in hello.addr so the peer can dial us
// back. Empty means the tailcat address if there is one, otherwise the
// first listen address. "none" disables it.
Advertise string
PingInterval time.Duration // idle time before a ping; default 30s
HandshakeTimeout time.Duration // default 30s
BlobLimit int64 // largest blob accepted or sent; default 50 MiB
OutboxTTL time.Duration // outbox retention; default 7 days
MaxBackoff time.Duration // reconnect backoff cap; default 60s
DialGrace time.Duration // delay before dialing a hello-learned address; default 15s
Policy Policy
// Logf receives the daemon's log. Nil discards it.
Logf func(format string, args ...any)
// TailcatLogf receives tailcat's (verbose) diagnostics. Nil discards them.
TailcatLogf func(format string, args ...any)
// Trace logs every line sent and received.
Trace bool
// AllowPlaintext permits plain TCP to public addresses (see
// transport.Transport.AllowPublicTCP).
AllowPlaintext bool
// Presence publishes this node's signed presence to its peers, who
// gossip it on (presence.go). Receiving and forwarding other agents'
// presence happens either way.
Presence bool
}
Config configures a Node. Zero values get the defaults from the spec.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is one connection to a peer, from hello to close.
type ConnInfo ¶
type ConnInfo struct {
Outbound bool `json:"outbound"`
Via string `json:"via"`
Since time.Time `json:"since"`
RTT time.Duration `json:"rtt"` // last ping's round trip; zero until measured
}
ConnInfo describes a live connection.
type ExecRequest ¶
type ExecRequest struct {
Cmd json.RawMessage `json:"cmd"` // ["argv", ...] or "shell string"
Cwd string `json:"cwd,omitempty"`
Env map[string]string `json:"env,omitempty"`
Timeout float64 `json:"timeout,omitempty"` // seconds, default 600
Stdin string `json:"stdin,omitempty"`
}
ExecRequest is the data of an application/vnd.awp.exec+json part.
type FSReadRequest ¶
type FSReadRequest struct {
Path string `json:"path"`
Offset int64 `json:"offset,omitempty"`
Length int64 `json:"length,omitempty"`
}
FSReadRequest is the data of an application/vnd.awp.fs-read+json part.
type FSWriteRequest ¶
type FSWriteRequest struct {
Path string `json:"path"`
Content string `json:"content"`
Encoding string `json:"encoding,omitempty"` // "utf-8" (default) or "base64"
Append bool `json:"append,omitempty"`
Mkdir bool `json:"mkdir,omitempty"`
}
FSWriteRequest is the data of an application/vnd.awp.fs-write+json part.
type Node ¶
type Node struct {
// contains filtered or unexported fields
}
Node is a running awp peer.
func Open ¶
Open loads (or creates) the identity and store under cfg.Home. Call Start to begin listening and reconnecting.
func (*Node) Activity ¶
Activity reports when the agent last acted (zero if not since the daemon started) and whether it is waiting for a message now.
func (*Node) Bye ¶
Bye closes the connection to a peer gracefully (section 9.6) and parks it: no reconnection until something new is queued for it.
func (*Node) Changed ¶
func (n *Node) Changed() <-chan struct{}
Changed returns a channel that is closed at the next state change: a log record, an ack, a connection coming or going. Waiters re-check their condition and call Changed again.
func (*Node) Close ¶
Close stops everything. Connections are dropped without bye: the state is on disk and peers will resume.
func (*Node) Connect ¶
Connect dials an address given out of band (section 4.1) and returns the key of the peer that answered. If a peer known at this address is already connected, that connection is reused.
func (*Node) Dialing ¶
Dialing reports whether a reconnect loop is running for key, and its last error.
func (*Node) Grant ¶
Grant mints a grant for sub (section 10.3), records it, and sends it if the peer is connected; otherwise it goes out after the next resume.
func (*Node) Introduce ¶
func (n *Node) Introduce(to, intro string, caps []string, ttl time.Duration, th string) (*SendResult, error)
Introduce hands peer `to` the identity and address of peer `intro`, with a grant `intro` will honor if it trusts us with introduce (section 10.4). The grant carries aud = intro so it confers nothing on anyone else.
func (*Node) LocalPresence ¶
LocalPresence describes this node's current activity. It is what the node would publish, minus the signature, and is available whether or not sharing is on.
func (*Node) MakePrivate ¶
MakePrivate keeps the thread th with peer out of conversation sharing, on both sides: this node stops mirroring it (and has its hosts forget it), and peer is asked to do the same.
func (*Node) ResolvePeer ¶
ResolvePeer turns what a user typed into a key: a full key, an alias, a name, or a unique prefix of the key's base64 body.
func (*Node) ResolveThread ¶
ResolveThread finds which peer a thread id belongs to.
func (*Node) Revoke ¶
Revoke forgets a grant we issued. Grants are bearer statements, so a copy the peer holds stays valid elsewhere until it expires; locally it stops being honored at once.
func (*Node) Send ¶
func (n *Node) Send(req SendRequest) (*SendResult, error)
Send queues a msg. It never fails because the peer is away: the message goes to the outbox on disk and is delivered on the next resume.
func (*Node) SetModel ¶
SetModel records the model the agent now runs on, remembers it, and republishes presence if it changed. It reports whether it changed.
func (*Node) SetShareWith ¶
SetShareWith sets the hosts to share conversations with (none to stop), remembers them, and republishes presence. Peers learn of the change in presence at once, and in hello on their next connection.
func (*Node) SetState ¶
func (n *Node) SetState(peer, th, state, note string) (*SendResult, error)
SetState sends our view of a thread's state (section 8.1).
func (*Node) ShareWith ¶
ShareWith returns the keys of the hosts this node mirrors its conversations to.
func (*Node) SharesPresence ¶
SharesPresence reports whether this node publishes its own presence.
func (*Node) Start ¶
Start opens the listeners and starts reconnecting to peers that have unfinished business. A tailcat listener starts in the background, since picking a DERP region takes a few seconds.
func (*Node) TailcatAddress ¶
TailcatAddress returns the bare tailcat address, if listening on tailcat.
type Policy ¶
type Policy struct {
Accept string `json:"accept,omitempty"` // AcceptAny or AcceptAllowlist
Allow []string `json:"allow,omitempty"` // keys admitted under the allowlist
Trust []string `json:"trust,omitempty"` // issuers whose grants we honor as our own
// Serve lists capabilities this daemon fulfils by itself when a peer
// with the matching grant asks (exec, fs:read, fs:write). Requests for
// capabilities not listed here are delivered to the agent instead,
// marked allowed or denied.
Serve []string `json:"serve,omitempty"`
// Root confines fs:read and fs:write and is the default working
// directory for exec. Empty means the daemon's working directory.
Root string `json:"root,omitempty"`
}
Policy is the local trust configuration.
type Request ¶
type Request struct {
Part int `json:"part"`
Mime string `json:"mime"`
Cap string `json:"cap"`
Allowed bool `json:"allowed"`
Served bool `json:"served,omitempty"`
}
Request is a capability request found in a received msg.
type SendRequest ¶
type SendRequest struct {
Peer string `json:"peer"`
Th string `json:"th,omitempty"` // empty starts a new thread
Subject string `json:"subject,omitempty"`
Re string `json:"re,omitempty"`
Parts []wire.Part `json:"parts,omitempty"`
Files []string `json:"files,omitempty"` // attached as blobs
// contains filtered or unexported fields
}
SendRequest describes a msg to send.