Documentation
¶
Overview ¶
Package nodedir implements Propagare's bounded, signed IP node directory.
The directory is a discovery and availability mechanism, not an anonymity mechanism. Node endpoints are intentionally public. Membership is admitted only through locally pinned seed identities and short-lived signed leases.
Index ¶
- Constants
- func AnnouncementHash(announcement Announcement) []byte
- func NewChallenge() ([]byte, error)
- func VerifyAnnouncement(announcement Announcement, now time.Time, allowPrivate bool) error
- func VerifyChallenge(identity protocol.NodePublicIdentity, nonce []byte, response ChallengeResponse, ...) error
- func VerifyRecord(policy Policy, record Record, now time.Time) error
- func VerifySnapshotHeader(snapshot Snapshot, expectedPublisherID string, now time.Time, maxNodes int) error
- type Agent
- func (agent *Agent) AcceptRegistration(ctx context.Context, remoteIP netip.Addr, request RegistrationRequest, ...) (Record, error)
- func (agent *Agent) Active(now time.Time) []Record
- func (agent *Agent) Challenge(nonce []byte, now time.Time) (ChallengeResponse, error)
- func (agent *Agent) IdentityID() string
- func (agent *Agent) Registry() *Registry
- func (agent *Agent) Run(ctx context.Context, interval time.Duration) error
- func (agent *Agent) Snapshot(now time.Time) (Snapshot, error)
- func (agent *Agent) SyncOnce(ctx context.Context, now time.Time) error
- type Announcement
- type Attestation
- type ChallengeRequest
- type ChallengeResponse
- type Endpoint
- type HTTPProber
- type PinnedNode
- type Policy
- type ReachabilityProber
- type Record
- type RegistrationRequest
- type Registry
- func (registry *Registry) Active(now time.Time) []Record
- func (registry *Registry) Merge(record Record, now time.Time) error
- func (registry *Registry) MergeSnapshot(snapshot Snapshot, expectedPublisherID string, now time.Time) error
- func (registry *Registry) Policy() Policy
- func (registry *Registry) Sweep(now time.Time) int
- type Signer
- type Snapshot
Constants ¶
const ( ProtocolVersion = 1 ChallengeBytes = 32 MaxDirectoryNodes = 512 MaxAttestations = 16 MaxSnapshotBytes = 16 * 1024 * 1024 MaxRegistrationBytes = 256 * 1024 MinLease = 10 * time.Minute MaxLease = 2 * time.Hour MaxClockSkew = 5 * time.Minute )
const MaxSeedFileBytes = 1024 * 1024
Variables ¶
This section is empty.
Functions ¶
func AnnouncementHash ¶
func AnnouncementHash(announcement Announcement) []byte
func NewChallenge ¶
func VerifyAnnouncement ¶
func VerifyAnnouncement(announcement Announcement, now time.Time, allowPrivate bool) error
func VerifyChallenge ¶
func VerifyChallenge(identity protocol.NodePublicIdentity, nonce []byte, response ChallengeResponse, now time.Time) error
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
func (*Agent) AcceptRegistration ¶
func (agent *Agent) AcceptRegistration(ctx context.Context, remoteIP netip.Addr, request RegistrationRequest, now time.Time) (Record, error)
AcceptRegistration proves that the request's TCP source owns the advertised IP, then performs a bounded callback challenge to the same IP and advertised port. Only a locally pinned seed identity can issue an admission attestation.
func (*Agent) IdentityID ¶
type Announcement ¶
type Announcement struct {
Version uint8 `json:"version"`
Identity protocol.NodePublicIdentity `json:"identity"`
Endpoint Endpoint `json:"endpoint"`
Sequence uint64 `json:"sequence"`
IssuedAt time.Time `json:"issued_at"`
ExpiresAt time.Time `json:"expires_at"`
Signature protocol.HybridSignature `json:"signature"`
}
type Attestation ¶
type Attestation struct {
Version uint8 `json:"version"`
AuthorityID string `json:"authority_id"`
AnnouncementHash []byte `json:"announcement_hash"`
ObservedAt time.Time `json:"observed_at"`
ExpiresAt time.Time `json:"expires_at"`
Signature protocol.HybridSignature `json:"signature"`
}
func SignAttestation ¶
func SignAttestation(signer Signer, announcement Announcement, now time.Time) (Attestation, error)
type ChallengeRequest ¶
type ChallengeRequest struct {
Nonce []byte `json:"nonce"`
}
type ChallengeResponse ¶
type ChallengeResponse struct {
Version uint8 `json:"version"`
NodeID string `json:"node_id"`
Nonce []byte `json:"nonce"`
CreatedAt time.Time `json:"created_at"`
Signature protocol.HybridSignature `json:"signature"`
}
func SignChallenge ¶
type Endpoint ¶
type HTTPProber ¶
func (*HTTPProber) Verify ¶
func (prober *HTTPProber) Verify(ctx context.Context, announcement Announcement) error
type PinnedNode ¶
type PinnedNode struct {
Identity protocol.NodePublicIdentity `json:"identity"`
Endpoint Endpoint `json:"endpoint"`
}
func DecodePinnedNodes ¶
func DecodePinnedNodes(data []byte) ([]PinnedNode, error)
func LoadPinnedNodes ¶
func LoadPinnedNodes(path string) ([]PinnedNode, error)
type Policy ¶
type Policy struct {
Seeds []PinnedNode
AuthorityQuorum int
AllowPrivateIPs bool
MaxNodes int
}
type ReachabilityProber ¶
type ReachabilityProber interface {
Verify(ctx context.Context, announcement Announcement) error
}
type Record ¶
type Record struct {
Announcement Announcement `json:"announcement"`
Attestations []Attestation `json:"attestations"`
}
func CloneRecord ¶
CloneRecord returns an ownership-independent copy of a verified directory record. Consumers which retain routes must not alias snapshot buffers that a caller can later mutate.
type RegistrationRequest ¶
type RegistrationRequest struct {
Record Record `json:"record"`
}
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
func NewRegistry ¶
func (*Registry) Merge ¶
Merge validates every signature before changing state. The monotonically increasing sequence prevents rollback to an older lease for the same node.
func (*Registry) MergeSnapshot ¶
type Signer ¶
type Signer interface {
PublicIdentity() protocol.NodePublicIdentity
Sign(domain string, message []byte) (protocol.HybridSignature, error)
}
type Snapshot ¶
type Snapshot struct {
Version uint8 `json:"version"`
Publisher protocol.NodePublicIdentity `json:"publisher"`
GeneratedAt time.Time `json:"generated_at"`
Records []Record `json:"records"`
Signature protocol.HybridSignature `json:"signature"`
}